1. 程式人生 > >BZOJ3569 DZY-Loves-Chinese-II

BZOJ3569 DZY-Loves-Chinese-II

Link

Difficulty

演算法難度5,思維難度7,程式碼難度5

Description

給定一個無向圖, n n 個點, m m 條邊, q

q 組詢問

每組詢問刪除 k k 條邊,問整張圖是否連通

強制線上

n 1 0

5 , m 5 × 1 0 5 ,
q 5 × 1 0 4 n\le 10^5,m\le 5\times 10^5,q\le 5\times 10^4

Solution

發現在 k k 條刪掉的邊中,只要刪掉其中一些邊就可以斷開這張圖,其他邊無關緊要

以下就開始神仙了

我們隨便取這張圖的一棵生成樹(dfs樹就可以的)

然後給非樹邊隨機一個非負權值,樹邊的權值就等於所有跨越它的非樹邊的權值異或和

如果有一組邊可以斷開這張圖,那麼一定存在一種方案使得:

從它們之中取一些邊,異或起來恰好為0(這些邊就是我說的那些必要的邊)

所以我們要判斷是否存在一種方案使得這些邊的異或和為0

這東西有個題是 k 4 k\le 4 來著,可以暴力列舉,但這個題不行了

我們可以使用線性基來判斷

每次插入之後判斷剩下的那個數是否等於0,如果等於0就說明不連通了

時間複雜度 O ( n + m + q l o g V ) O(n+m+qlogV)

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstring>
#include<ctime>
#define LL long long
using namespace std;
inline int read(){
    int x=0,f=1;char ch=' ';
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return f==1?x:-x;
}
const int N=1e5+5,M=1e6+5;
inline double Rand(){
	return (double)rand()/(double)RAND_MAX;
}
int n,m,q,tot;
int head[N],to[M],Next[M],val[M],is[M];
inline void addedge(int x,int y,int c){
	to[++tot]=y;
	Next[tot]=head[x];
	head[x]=tot;
	val[tot]=c;
}
int X[M],Y[M],V[M];
int vis[N],dfn[N],dfn_clock,ed[N];
inline void dfs(int x,int fa){
	vis[x]=1;
	dfn[x]=++dfn_clock;
	for(int i=head[x];i;i=Next[i]){
		int u=to[i];
		if(u==fa || vis[u])continue;
		is[i]=1;
		dfs(u,x);
	}
	ed[x]=dfn_clock;
}
int t[N];
inline void dfs2(int x,int fa){
	vis[x]=1;
	for(int i=head[x];i;i=Next[i]){
		int u=to[i];
		if(u==fa || vis[u])continue;
		dfs2(u,x);
		t[x]^=t[u];
		val[i]=t[u];
		if(i&1)val[i+1]=t[u];
		else val[i-1]=t[u];
	}
}
int b[32];
int main(){
	srand(time(0));
	n=read();m=read();
	for(int i=1;i<=m;++i){
		int x=read(),y=read(),c=Rand()*1e9+1;
		addedge(x,y,c);addedge(y,x,c);
		X[i]=x;Y[i]=y;V[i]=c;
	}
	dfs(1,0);
	for(int i=1;i<=m;++i){
		if(is[2*i] || is[2*i-1])continue;
	    t[X[i]]^=V[i];
		t[Y[i]]^=V[i];
	}
	for(int i=1;i<=n;++i)vis[i]=0;
	dfs2(1,0);
	int ans=0;
	q=read();
	for(int s=1;s<=q;++s){
		int k=read(),flag=1;
		for(int i=0;i<=31;++i)b[i]=0;
		while(k--){
			int x=read()^ans;
			x=val[x*2];
			for(int i=31;i>=0;--i){
				if((x>>i)&1){
					if(!b[i]){b[i]=x;break;}
					x^=b[i];
				}
			}
			if(!x)flag=0;
		}
		if(flag){printf("Connected\n");ans++;}
		else printf("Disconnected\n");
	}
    return 0;
}