【CF603E】Pastoral Oddities cdq分治+并查集
【CF603E】Pastoral Oddities
题意:有n个点,依次加入m条边权为$l_i$的无向边,每次加入后询问:当前图是否存在一个生成子图,满足所有点的度数都是奇数。如果有,输出这个生成子图中边权最大的边的权值最小可能是多少。
$n\le 10^5,m\le 10^6,l_i\le 10^9$
题解:可以证明如果存在一个生成子图满足所有点度数都是奇数,当且仅当所有连通块都有偶数个点。并且可以知道加边一定不会使答案更劣。正解有三种:1.LCT维护最小生成树;2.cdq分治(类似整体二分);3.线段树(类似按时间分治)。都比较神,本人采用了第二种。
官方题解:http://codeforces.com/blog/entry/21914
大神的第二种做法的题解:https://www.cnblogs.com/galaxies/p/cf603E.html
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn=100010; const int maxm=300010; int f[maxn],g[maxn],siz[maxn],st[maxn],ans[maxm]; int n,m,cnt,top; struct edge { int a,b,c,tim; }p[maxm],q[maxm]; bool cmp(const edge &a,const edge &b) { return (a.c==b.c)?(a.tim<b.tim):(a.c<b.c); } inline void uni(int a,int b) { int x=a,y=b,c=0,d=0; while(f[x]!=x) x=f[x],c++; while(f[y]!=y) y=f[y],d++; if(x==y) return ; if(c>d) swap(x,y),swap(a,b); cnt-=(siz[x]&1)+(siz[y]&1)-((siz[x]+siz[y])&1); siz[y]+=siz[x],f[x]=y; st[++top]=x; } inline void del(int x) { int y=f[x]; siz[y]-=siz[x],f[x]=x; cnt+=(siz[x]&1)+(siz[y]&1)-((siz[x]+siz[y])&1); } void solve(int l,int r,int L,int R) { if(l>r) return ; int mid=(l+r)>>1,i,now=top,MID; for(i=l;i<=mid;i++) if(p[i].c<=L) uni(p[i].a,p[i].b); for(i=L;i<=R&&cnt;i++) if(q[i].tim<=mid) uni(q[i].a,q[i].b); MID=max(L,i-1); if(!cnt) ans[p[mid].tim]=q[MID].c; else ans[p[mid].tim]=-1; while(top>now) del(st[top--]); for(i=L;i<=MID;i++) if(q[i].tim<=l) uni(q[i].a,q[i].b); solve(l,mid-1,MID,R); while(top>now) del(st[top--]); for(i=l;i<=mid;i++) if(p[i].c<=L) uni(p[i].a,p[i].b); solve(mid+1,r,L,MID); while(top>now) del(st[top--]); } inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-') f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+(gc^'0'),gc=getchar(); return ret*f; } int main() { n=rd(),m=rd(); int i; for(i=1;i<=m;i++) p[i].a=rd(),p[i].b=rd(),p[i].c=rd(),p[i].tim=i,q[i]=p[i]; sort(q+1,q+m+1,cmp); for(i=1;i<=n;i++) f[i]=i,siz[i]=1; for(i=1;i<=m;i++) p[q[i].tim].c=i; cnt=n; solve(1,m,1,m); for(i=1;i<=m;i++) printf("%d\n",ans[i]); return 0; }
| 欢迎来原网站坐坐! >原文链接<