6733. 【2020.06.18省選模擬】T1 無向圖

題目


正解

似乎曾經某次模擬賽見過?

首先可以發現刪去的邊不存在環,因爲這樣對度數的奇偶性沒有影響。
於是刪去的邊形成一個森林。

先講2n,m=n12|n,m=n-1的部分分:
隨便找某個節點作爲根,然後從葉子節點往上做。對於每個點,可以通過它的父親邊選或不選使得它的度數保持爲奇數。對於根節點,可以計算出它最終的度數也是奇數。
然後發現不管以哪個點作爲根的時候,方案都是唯一的。

還有一種優秀的理解方法:
建出樹之後,找到兩個度數爲偶數的節點,將它們路徑上的邊的狀態都取反。容易發現中間的點的度數奇偶性不變,這兩個節點的度數都變成了奇數。
只要偶數個數爲偶數,這樣操作若干次之後,全部點都會是奇數度數。

然後就是2n2|n的部分分:
爲了使得字典序最小,考慮從後往前生成樹。不在生成樹中的邊都欽定它們保留。
然後直接在這個生成樹上操作。方案唯一,所以是對的。
但是當nn爲奇數的時候,這個方法就會有鍋。因爲在生成樹之後,選取的根不同會生成不同的答案。
接下來考慮按順序欽定每條邊選或不選:
對於最小的邊,求出它連的兩個連通塊中偶數點個數的奇偶性。
如果都是奇數,則這條邊必須被刪;否則這條邊被保留。
接下來不需要考慮這條邊,然後變成了兩個子問題,遞歸下去考慮。
具體實現可以從後往前建出重構樹,然後用樹狀數組維護子樹中偶數點的奇偶性。


代碼

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 600010
#define M 900010
int n,m,cnt;
struct edge{
	int u,v;
} ed[M];
int deg[N];
int dsu[N*2];
int getdsu(int x){return dsu[x]==x?x:dsu[x]=getdsu(dsu[x]);}
int to[N*2][2];
int ide[N*2];
int nowdfn,in[N*2],out[N*2];
int t[N];
void xo(int x,int c){
	for (;x<=n;x+=x&-x)
		t[x]^=c;
}
int query(int x){
	int r=0;
	for (;x;x-=x&-x)
		r^=t[x];
	return r;
}
void init(int x){
	if (x<=n){
		in[x]=out[x]=++nowdfn;
		return;
	}
	init(to[x][0]);
	init(to[x][1]);
	in[x]=in[to[x][0]];
	out[x]=out[to[x][1]];
}
int getxor(int x){
	return query(out[x])^query(in[x]-1);
}
int ans[M];
void dfs(int x){
	if (x<=n)
		return;
	if (getxor(to[x][0]) && getxor(to[x][1])){
		int u=ed[ide[x]].u,v=ed[ide[x]].v;
		deg[u]^=1,xo(in[u],1);
		deg[v]^=1,xo(in[v],1);
		ans[ide[x]]=0;
	}
	else
		ans[ide[x]]=1;
	dfs(to[x][0]);
	dfs(to[x][1]);
}
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	freopen("graph.in","r",stdin);
	freopen("graph.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;++i){
		scanf("%d%d",&ed[i].u,&ed[i].v);
		deg[ed[i].u]^=1;
		deg[ed[i].v]^=1;
	}
	for (int i=1;i<=n;++i)
		dsu[i]=i;
	cnt=n;
	for (int i=m;i>=1;--i){
		int x=getdsu(ed[i].u),y=getdsu(ed[i].v);
//		printf("%d %d\n",x,y);
		if (x==y)
			ans[i]=1;
		else{
			++cnt;
			ide[cnt]=i;
			dsu[cnt]=cnt;
			dsu[x]=dsu[y]=cnt;
			to[cnt][0]=x;
			to[cnt][1]=y;
//			printf("%d:%d %d\n",cnt,x,y);
		}
	}
	int rt=cnt;
	init(rt);
	for (int i=1;i<=n;++i)
		if (deg[i]==0)
			xo(in[i],1); 
	dfs(rt);
	for (int i=1;i<=m;++i)
		putchar(ans[i]+'0');
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章