现在的位置: 首页 > 综合 > 正文

BZOJ 3563 DZY Loves Chinese / BZOJ 3569 DZY Loves Chinese II 随机化+高斯消元解异或方程组

2017年11月21日 ⁄ 综合 ⁄ 共 1732字 ⁄ 字号 评论关闭

题目大意:给出一个无向图,问删掉k条边的时候,图是否联通。

思路:虽然我把这两个题放在了一起,但是其实这两个题可以用完全不同的两个解法来解决。

第一个题其实是DZY出错了。。。把每次的边数也异或了,那就直接用这个性质一个一个往后推就行了。。最后一个暴力求一下。。

第二个题才是本意啊。

听到做法的时候我惊呆了。。

首先是将整个图中拆出一个树,那么所有边就分为树边和非树边。将所有非树边都加一个随机权值。树边的权值是所有能够覆盖它的非树边的权值的异或和。

把整个图拆开的充要条件是拆掉一条树边,同时将所有覆盖它的非树边也要拆掉,这些边的异或和正好等于0.这个就可以用高斯消元解异或方程组来解决了。

神啊神啊神啊神啊。。。

CODE:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 1000010
using namespace std;

struct Edge{
	int x,y,len;
	bool tree_edge;
	
	void Read() {
		scanf("%d%d",&x,&y);
	}
}edge[MAX];

int points,edges,asks;
int head[MAX],total = 1;
int next[MAX],aim[MAX];

bool v[MAX];
int _xor[MAX];

inline void Add(int x,int y)
{
	next[++total] = head[x];
	aim[total] = y;
	head[x] = total;
}

void BuildTree(int x,int last)
{
	v[x] = true;
	for(int i = head[x]; i; i = next[i])
		if(!v[aim[i]]) {
			edge[i >> 1].tree_edge = true;
			BuildTree(aim[i],x);
		}
}

void DFS(int x,int last)
{
	for(int i = head[x]; i; i = next[i])
		if(edge[i >> 1].tree_edge && aim[i] != last) {
			DFS(aim[i],x);
			edge[i >> 1].len = _xor[aim[i]];
			_xor[x] ^= _xor[aim[i]];
		}
}

int temp[MAX];

inline bool GaussElimination(int cnt)
{
	int k = 0,i;
	for(int j = 1 << 30; j; j >>= 1) {
		for(i = k + 1; i <= cnt; ++i)
			if(temp[i]&j)
				break;
		if(i == cnt + 1)	continue;
		swap(temp[i],temp[++k]);
		for(int i = 1; i <= cnt; ++i)
			if(temp[i]&j && i != k)
				temp[i] ^= temp[k];
	}
	return temp[cnt];
}

int main()
{
	srand(19970815);
	cin >> points >> edges;
	for(int i = 1; i <= edges; ++i) {
		edge[i].Read();
		Add(edge[i].x,edge[i].y);
		Add(edge[i].y,edge[i].x);
	}
	BuildTree(1,0);
	for(int i = 1; i <= edges; ++i)
		if(!edge[i].tree_edge) {
			edge[i].len = rand();
			_xor[edge[i].x] ^= edge[i].len;
			_xor[edge[i].y] ^= edge[i].len;
		}
	DFS(1,0);
	cin >> asks;
	int last_ans = 0;
	for(int cnt,x,i = 1; i <= asks; ++i) {
		scanf("%d",&cnt);
		//cnt ^= last_ans;
		for(int j = 1; j <= cnt; ++j) {
			scanf("%d",&x);
			x ^= last_ans;
			temp[j] = edge[x].len;
		}
		bool flag = !GaussElimination(cnt);
		if(!flag) {
			puts("Connected");
			++last_ans;
		}
		else	puts("Disconnected");
	}
	return 0;
}

抱歉!评论已关闭.