6733. 【2020.06.18省选模拟】T1 无向图

题目


正解

似乎曾经某次模拟赛见过?

首先可以发现删去的边不存在环,因为这样对度数的奇偶性没有影响。
于是删去的边形成一个森林。

先讲 2 ∣ n , m = n − 1 2|n,m=n-1 2n,m=n1的部分分:
随便找某个节点作为根,然后从叶子节点往上做。对于每个点,可以通过它的父亲边选或不选使得它的度数保持为奇数。对于根节点,可以计算出它最终的度数也是奇数。
然后发现不管以哪个点作为根的时候,方案都是唯一的。

还有一种优秀的理解方法:
建出树之后,找到两个度数为偶数的节点,将它们路径上的边的状态都取反。容易发现中间的点的度数奇偶性不变,这两个节点的度数都变成了奇数。
只要偶数个数为偶数,这样操作若干次之后,全部点都会是奇数度数。

然后就是 2 ∣ n 2|n 2n的部分分:
为了使得字典序最小,考虑从后往前生成树。不在生成树中的边都钦定它们保留。
然后直接在这个生成树上操作。方案唯一,所以是对的。
但是当 n n n为奇数的时候,这个方法就会有锅。因为在生成树之后,选取的根不同会生成不同的答案。
接下来考虑按顺序钦定每条边选或不选:
对于最小的边,求出它连的两个连通块中偶数点个数的奇偶性。
如果都是奇数,则这条边必须被删;否则这条边被保留。
接下来不需要考虑这条边,然后变成了两个子问题,递归下去考虑。
具体实现可以从后往前建出重构树,然后用树状数组维护子树中偶数点的奇偶性。


代码

using namespace std;
#include 
#include 
#include 
#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;
}

你可能感兴趣的:(图论)