zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    laravel如何自定义一个路由文件
    RGAC-laravel权限管理
    导航栏下划线跟随
    php简单的判断用户登陆
    php判断文件上传的类型
    git 如何删除仓库中的文件
    laravle中config( )函数的使用
    [leetcode] Count and Say
    [leetcode] Sudoku Solver
    [leetcode] Search Insert Position
  • 原文地址:https://www.cnblogs.com/jz-597/p/14679035.html
Copyright © 2011-2022 走看看