• CF603EPastoral Oddities【CDQ分治,可撤销并查集】


    正题

    题目链接:https://www.luogu.com.cn/problem/CF603E


    题目大意

    开始时有\(n\)个点,没有边。

    依次加入\(m\)条带权的边,每次加入后询问是否存在一个边集,满足每个点的度数均为奇数,求使得这个边集的最大权值最小。

    \(1\leq n\leq 10^5,1\leq m\leq 3\times 10^5\)


    解题思路

    首先考虑存在这个边集的条件,可以证明存在满足条件的边集的充要条件是联通块的大小都是偶数。
    必要性:对于一个联通块,因为每条边都会贡献偶数个度数,而如果这个连通块是奇数个点,那么如果合法的总度数就是 奇数×奇数=奇数 ,显然不可能是偶数,所以不存在这种情况。
    充分性:如果存在一个点的度数为奇数,那么这个联通快里也至少有一个点的度数是奇数,我们顺路删掉这两个点路径上的边就可以调整到合法情况。

    而我们能连边就连边肯定是最优的,因为不存在一种连边会使得奇数连通块数变多。

    然后考虑用CDQ分治解决这题,注意到答案肯定是单调不升的,我们的流程是记录目前区间\([l,r]\)的答案区间\(\{L,R\}\)

    先计算出\(ans_{mid}\),那么此时我们就可以分为\([l,mid-1]\{ans_{mid},R\}\)\([mid+1,r]\{L,ans_{mid}\}\)

    此时两个区间都被分开,这提示我们暴力枚举这些区间就是正常分治的复杂度。

    那么做法就很显然了,我们算出\(ans_{mid}\)后左右两边递归处理,用可撤销并查集处理。

    时间复杂度:\(O(m\log m\log n)\)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=3e5+10;
    struct node{
    	int x,y,w,id;
    }a[N],b[N];
    struct clnode{
    	int x,y,siz,dep;
    }cl[N];
    int n,m,sum,clt,fa[N],siz[N],dep[N];
    int ans[N],rk[N];
    int find(int x)
    {return (fa[x]==x)?x:find(fa[x]);}
    void unionn(int x,int y){
    	x=find(x);y=find(y);
    	if(x==y)return;
    	if(dep[x]>dep[y])swap(x,y);
    	cl[++clt]=(clnode){x,y,siz[y],dep[y]};
    	sum-=(siz[x]&1)&(siz[y]&1);
    	fa[x]=y;siz[y]+=siz[x];
    	dep[y]=max(dep[y],dep[x]+1);
    }
    void clearto(int d){
    	while(clt>d){
    		int x=cl[clt].x,y=cl[clt].y;
    		siz[y]=cl[clt].siz;
    		dep[y]=cl[clt].dep;
    		sum+=(siz[x]&1)&(siz[y]&1);
    		fa[x]=x;clt--;
    	}
    	return;
    }
    void cdq(int l,int r,int L,int R){
    	if(l>r)return;
    	int mid=(l+r)>>1,now=clt;
    	for(int i=l;i<=mid;i++)
    		if(rk[i]<L)unionn(a[i].x,a[i].y);
    	int mow=clt;
    	for(int i=L;i<=R;i++){
    		if(b[i].id<=mid)unionn(b[i].x,b[i].y);
    		if(!sum){ans[mid]=i;break;}
    	}
    	if(!ans[mid]){
    		clearto(mow);
    		cdq(mid+1,r,L,R);
    		return;
    	}
    	clearto(mow);
    	cdq(mid+1,r,L,ans[mid]);
    	clearto(now);
    	for(int i=L;i<ans[mid];i++)
    		if(b[i].id<l)unionn(b[i].x,b[i].y);
    	cdq(l,mid-1,ans[mid],R);
    	clearto(now);return;
    }
    bool cmp(node x,node y)
    {return x.w<y.w;}
    int main()
    {
    	scanf("%d%d",&n,&m);sum=n/2;
    	if(n&1){
    		for(int i=1;i<=m;i++)
    			puts("-1");
    		return 0;
    	}
    	for(int i=1;i<=n;i++)fa[i]=i,siz[i]=1;
    	for(int i=1;i<=m;i++){
    		scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].w);
    		a[i].id=i;b[i]=a[i];
    	}
    	sort(b+1,b+1+m,cmp);
    	for(int i=1;i<=m;i++)rk[b[i].id]=i;
    	cdq(1,m,1,m);
    	for(int i=1;i<=m;i++)
    		if(!ans[i])puts("-1");
    		else printf("%d\n",b[ans[i]].w);
    	return 0;
    }
    
  • 相关阅读:
    Javascript高级篇-Function对象
    Object类、instanceof
    [一]Head First设计模式之【策略模式】(鸭子设计的优化历程)
    匿名内部类
    设计模式之单例模式
    长江商业评论读书笔记
    [转]Freemarker数据类型转换
    面向对象编程——概论(一)
    IP地址处理模块IPy
    系统性能模块psutil
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/15890278.html
Copyright © 2020-2023  润新知