CF1508C. Complete the MST

有个完全图,其中(m)条边的权值已经被确定。现在要确定其它边的权值,要求最终所有权值异或和为(0)。最小化权值和。

(n,mle 2*10^5)


称没有被确定权值的边为零边。设已经被确定的权值的异或和为(W)

无视限制,把零边的边权设为(0),然后跑MST。如果存在零边不在MST内,那就把这条零边的权值设为(W)。显然此时最优。如果(W=0),显然此时也最优。

否则:零边全在MST内,(W eq 0)。以下证明,存在一种方案,只有一个零边权值为(W),其它权值为(0),此时最优。

假设存在种最优的不满足这个条件的对零边赋值的方案,跑MST。如果存在一个零边不在MST内,则令其它的零边权值为(0),它的权值为(W),答案不会更劣;否则,任选一条零边,其它零边权值为(0),它的权值为(W),答案不变。

做法分为两部分:1. 对零边建出一棵生成森林。2. 枚举权值为(W)的零边,计算MST。

第一部分:

我的做法:用并查集维护连通块。枚举点(x)(x)要和(O(deg_x))个编号区间连边。维护(nxt_i)表示([i,nxt_i])都在一个连通块内,也可以用并查集来维护。现在给某个编号区间连边,把这个区间里原来在不同连通块内的各个区间分别取个点连,然后更新(nxt)。这样时间是(O(m))(并查集时间忽略)的。

dyp的做法:对于每个连通块维护个线段树,线段树节点记编号区间中是否存在点不在连通块内。每次遍历线段树,找到一个不在连通块内的点,然后进行线段树合并。时间(O(mlg n))。可以优化:枚举起点,维护个链表,这个链表中存下不在和起点同连通块内的点。设当前点为(x),遍历链表,对于和(x)不相邻的点,将其加入连通块并从链表中删去,并把这个点加入队列(或dfs)。时间(O(m))

gmh77的做法:找到度数最小的点(r),用它暴力连边。接下来和它不在同个连通块内的点的个数为(deg_r)(deg_rle frac{2m}{n})。对于这(deg_r)个点分别暴力连边,于是连了(O(frac{2m}{n} n))条边,就是(O(m))的了。

第二部分:

我的做法:如果生成森林中没有用到所有的零边,答案已经确定;否则,(m+cnt_{零边}=frac{n(n-1)}{2}),所以(n=O(sqrt m))。枚举(O(n))个零边作为(W),暴力跑MST,朴素实现(O(nm))。优化:实际上可以先对确定边跑MST,用在MST中的边跑这里的MST,时间(O(n^2))。于是时间是(O(min(n^2,m)))

dyph和gmh77的做法:考虑计算权值为(W)的边不在最终的MST的情况。对确定边跑MST,合并零边和确定边的MST。如果有某个确定边,它在确定边的MST中而不在零边和确定边的MST中,说明存在一个零边将其替换,换句话说也可以用它来替换这个零边。于是找到这样的边的最小边权,用它替换权值为(W)的邻边。


using namespace std;
#include <bits/stdc++.h>
#define N 200005
#define ll long long
int n,m;
int xo;
int deg[N];
struct edge{int u,v,w;} ed[N+N];
bool cmped(edge x,edge y){return x.w<y.w;}
vector<int> nei[N];
set<pair<int,int> > bz;
edge ep[N];
int cnt;
int dsu[N];
int getdsu(int x){return dsu[x]==x?x:dsu[x]=getdsu(dsu[x]);}
ll MST(int ban){
	ll ans=0;
	for (int i=1;i<=n;++i)
		dsu[i]=i;
	for (int i=1;i<=cnt;++i){
		if (i==ban) continue;
		int u=getdsu(ep[i].u),v=getdsu(ep[i].v);
		if (u!=v)
			dsu[u]=v;
	}
	if (ban && xo<=ed[1].w){
		int u=getdsu(ep[ban].u),v=getdsu(ep[ban].v);
		if (u!=v)
			dsu[u]=v,ans+=xo;
	}
	for (int i=1;i<=m;++i){
		int u=getdsu(ed[i].u),v=getdsu(ed[i].v);
		if (u!=v)
			dsu[u]=v,ans+=ed[i].w;
		if (ban && ed[i].w<xo && (i==m || xo<=ed[i+1].w)){
			int u=getdsu(ep[ban].u),v=getdsu(ep[ban].v);
			if (u!=v)
				dsu[u]=v,ans+=xo;
		}
	}
	return ans;
}
int nxt[N];
int getnxt(int x){return x==nxt[x]?x:nxt[x]=getnxt(nxt[x]);}
void merge(int l,int r,int c){
	if (l>r) return;
	if (l<=c && c<=r){
		merge(l,c-1,c);
		merge(c+1,r,c);
		return;
	}
	if (getdsu(l)!=getdsu(c)){
		dsu[getdsu(l)]=getdsu(c);
		if (bz.find(make_pair(l,c))==bz.end())
			bz.insert(make_pair(c,l)),ep[++cnt]={c,l,0};
	}
	while (getnxt(l)<r){
		int x=getnxt(l)+1;
		nxt[x-1]=x;
		if (getdsu(x)!=getdsu(c)){
			dsu[getdsu(x)]=getdsu(c);
			if (bz.find(make_pair(x,c))==bz.end())
				bz.insert(make_pair(c,x)),ep[++cnt]={c,x,0};
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	if (m==0){
		printf("0
");
		return 0;
	}
	for (int i=1;i<=m;++i){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		ed[i]={u,v,w};
		xo^=w;
		deg[u]++,deg[v]++;
		nei[u].push_back(v);
		nei[v].push_back(u);
	}
	sort(ed+1,ed+m+1,cmped);
	for (int i=1;i<=n;++i)
		dsu[i]=i,nxt[i]=i;
	nxt[n+1]=n+1;
	for (int i=1;i<=n;++i){
		if (deg[i]==n-1) continue;
		sort(nei[i].begin(),nei[i].end());
		if (deg[i]>0){
			merge(1,nei[i][0]-1,i);
			merge(nei[i][deg[i]-1]+1,n,i);
			for (int j=0;j<deg[i]-1;++j)
				merge(nei[i][j]+1,nei[i][j+1]-1,i);
		}
		else
			merge(1,n,i);
	}
	if (cnt+m<(ll)n*(n-1)/2){
		ll ans=MST(0);
		printf("%lld
",ans);
		return 0;
	}
	ll ans=LLONG_MAX;
	for (int i=1;i<=cnt;++i)
		ans=min(ans,MST(i));
	printf("%lld
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/jz-597/p/14679035.html