【bzoj3569】DZY Loves Chinese II 线性基+树上小技巧

首先找到一棵dfs树,注意是dfs树,因为dfs树没有横叉边,所以好处理
给每条非树边定一个随机权值,树边的权值就是所有覆盖这条树边的边的权值的异或和
覆盖这条树边,即有一条边的两端分别位于这棵树去掉这条树边所形成的两个联通块中
如果这个集合是线性无关的,则最终的图是连通的,否则是不连通的
于是使用线性基来判断是否是线性无关即可
如何处理一条树边的权值?

因为dfs树只有返祖边,所以对于每条非树边,把两个端点打上标记,最后再dfs一遍就好


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#define maxn 500010
#define maxm 500010

using namespace std;

int head[maxn],to[maxm],next[maxm],a[maxm],b[maxn];
int n,m,T,ans,num,k;
bool vis[maxn];
int c[40];

void addedge(int x,int y)
{
	num++;to[num]=y;next[num]=head[x];head[x]=num;
}

void dfs1(int x)
{
	vis[x]=1;
	for (int p=head[x];p;p=next[p])
	  if (!vis[to[p]]) dfs1(to[p]);
	  else
	  {
	  	a[p]=rand();
	  	b[x]^=a[p];b[to[p]]^=a[p];
	  }
}

void dfs2(int x)
{
	vis[x]=1;
	for (int p=head[x];p;p=next[p])
	  if (!vis[to[p]])
	  {
		dfs2(to[p]);
		a[p]^=b[to[p]];
		b[x]^=b[to[p]];
	  }
}

bool check()
{
	int now=0;
	for (int i=1<<30;i;i>>=1)
	{
		int j=now+1;
		for (;j<=k;j++) if (c[j]&i) break;
		if (j==k+1) continue;
		swap(c[j],c[++now]);
		for (int j=1;j<=k;j++)
		  if (j!=now && c[j]&i) c[j]^=c[now]; 
	}
	if (c[k]) return 0; else return 1;
}

int main()
{
	srand(19990610);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		if (x>y) swap(x,y);
		addedge(x,y);
	}
	dfs1(1);
	memset(vis,0,sizeof(vis));
	dfs2(1);
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d",&k);
		for (int i=1;i<=k;i++)
		{
			int x;
			scanf("%d",&x);
			x^=ans;c[i]=a[x];
		}
		if (check()) printf("Disconnected\n");
		else {ans++;printf("Connected\n");}
	}
	return 0;
}


你可能感兴趣的:(【bzoj3569】DZY Loves Chinese II 线性基+树上小技巧)