[2020.6.6多校省选模拟4]最简单题
Posted Tan_tan_tann
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[2020.6.6多校省选模拟4]最简单题相关的知识,希望对你有一定的参考价值。
最 简 单 题 \\color{red}{最简单题} 最简单题
题解
魔鬼删除线
做对这道题,首先我们要正确理解它的题意。
它是想要我们找到在 所有 的叶子节点匹配法方中最优的一种使其割边得到的无环,
1
1
1与
1
′
1'
1′不连通的森林需要割的边的最小边权和最小。
后面一部分显然可以理解成整个图的最大生成树。
对于
1
1
1与
1
′
1'
1′不联通的性质处理,我们可以考虑最开始就将
1
1
1和
1
′
1'
1′连起来,这样之后就不会再链接
1
1
1与
1
′
1'
1′两个联通块了。
在这之后,我们将整个最大生成树按
1
1
1与
1
′
1'
1′的边剖开,就可以得到我们要求的图。
由于我们
1
1
1与
1
′
1'
1′必然分出处两个连通块,如果还有第三的连通块,将其与其中一个连通块连起来明显是更优的,所以这样剪出来的最大生成树一定是更优的。
关键在于我们给出的图最开始两边的叶子是未曾进行匹配的,我们不可能去枚举叶子怎么匹配,那样就是
O
(
n
!
)
O(n!)
O(n!)级别的了。
但我们的任务实际上是找到一种合法的最大匹配,连接
m
m
m(一棵树上的叶子数)条边,也就意味着减少
m
m
m个连通块。
也就说明再这之前含叶子的连通块也就
m
+
1
m+1
m+1个。
实际上总的连通块也应该是
m
+
1
m+1
m+1个,因为我们的最优解一定可以保证最后得到答案时一定只有一个连通块。
而当我们含叶子的连通块有
m
+
1
m+1
m+1个时是一定可以保证无环并有解的,由根所在的连通块先向叶子数最多的联通块连边,一直这样连下去即可。
由于在不练这些边之前除了根所在的连通块以为,其它连通块都是分处两侧的,当然可以这样进行,而我们根的连通块也是至少含有一边的叶节点的。
我们连的
m
−
1
m-1
m−1条边要么是让一侧只有一个连通块,一侧的连通块通通只含一个叶子节点,可以像菊花图一样连边,要么两侧都有度数大于
1
1
1的连通块,我们一定是可以一直这样连下去直到全部都属于一个连通块。
实际上我们的任务是找到刚好有
m
+
1
m+1
m+1个含叶子的联通块时所需要连接的最小边权和。
这当然可以用
K
r
u
s
k
a
l
Kruskal
Kruskal求解,我们只需加上一个判断当前含叶子的联通块个数的特判即可。
我们很容易证明用
K
r
u
s
k
a
l
Kruskal
Kruskal的求解方法是正确的,因为对于含叶子节点的联通块之间我们只会连接
m
−
1
m-1
m−1条边。
我们先不考虑不含叶子节点的连通块之间的连边,或者就将它们全部附在与自己最优的含叶子节点的联通块上,相当于得到一个森林。
而此时我们得到的新图明显与有没有含不含叶子节点的连通块是没有关系的,可以直接连边。
这同时也是符合
K
r
u
s
k
a
l
Kruskal
Kruskal的性质的,两者是独立的。
时间复杂度 O ( n l o g n ) O\\left(nlog\\,n\\right) O(nlogn)
源码
#include<bits/stdc++.h>
using namespace std;
#define MAXN 200005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
const LL INF=0x7f7f7f7f7f7f;
const int mo=1e8+7;
const int jzm=2333;
const int lim=1000000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-9;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;}return t;}
int n,tot,deg1[MAXN],deg2[MAXN],fa[MAXN];bool leaf[MAXN];
struct ming{int u,v;LL w;bool operator < (const ming &rhs)const{return w<rhs.w;}}s[MAXN];
void makeSet(int x){for(int i=1;i<=x;i++)fa[i]=i;}
int findSet(int x){return x==fa[x]?x:fa[x]=findSet(fa[x]);}
signed main(){
freopen("C.in","r",stdin);
freopen("C.out","w",stdout);
read(n);LL summ=0;int cnt=0,num=0;
if(n==1){puts("0");return 0;}
for(int i=1;i<n;i++){
int u,v;LL w;read(u);read(v);read(w);
s[++tot]=(ming){u,v,w};summ+=w;deg1[u]++;deg1[v]++;
}
for(int i=1;i<n;i++){
int u,v;LL w;read(u);read(v);read(w);
s[++tot]=(ming){u+n,v+n,w};summ+=w;deg2[u]++;deg2[v]++;
}
makeSet(2*n);sort(s+1,s+tot+1);fa[n+1]=1;
for(int i=2;i<=n;i++){
if(deg1[i]==1)leaf[i]=1,cnt++;
if(deg2[i]==1)leaf[i+n]=1,cnt++;
}
num=cnt/2;
for(int i=tot;i>0;i--){
int u=findSet(s[i].u),v=findSet(s[i].v);if(u==v)continue;
if(leaf[u]&&leaf[v]&&cnt==num+1)continue;
summ-=s[i].w;cnt-=(leaf[u]&&leaf[v]);leaf[v]|=leaf[u];fa[u]=v;
}
printf("%lld\\n",summ);
return 0;
}
谢谢!!!
以上是关于[2020.6.6多校省选模拟4]最简单题的主要内容,如果未能解决你的问题,请参考以下文章