CF603E-Pastoral Oddities【CDQ分治,可撤销并查集】

1|0正题

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


1|1题目大意

开始时有n个点,没有边。

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

1n105,1m3×105


1|2解题思路

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

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

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

先计算出ansmid,那么此时我们就可以分为[l,mid1]{ansmid,R}[mid+1,r]{L,ansmid}

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

那么做法就很显然了,我们算出ansmid后左右两边递归处理,用可撤销并查集处理。

时间复杂度:O(mlogmlogn)


1|3code

#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; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/15890278.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示