1. 程式人生 > 其它 >CF603E-Pastoral Oddities【CDQ分治,可撤銷並查集】

CF603E-Pastoral 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;
}