CF603E-Pastoral Oddities【CDQ分治,可撤銷並查集】
阿新 • • 發佈:2022-02-13
正題
題目連結: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; }