Bzoj3562 神器化合物[Shoi 2014]

AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=3562

[分析]

若把每一个原子看作一个节点,将化学键看作一条边,那么这个题目要求的“分子的个数”很容易就可以看出是求图中联通块的个数。

求联通块的个数,可以使用并查集。可如何求出每一步的联通块的个数呢?

可以知道,当连上一条边时,若此边连接的是两个不同的联通块,那么分子个数就会减一;当删去一条边时,若删去这条边后,它两边的边不连通了,则分子个数就会加一。

如何判断是否连通呢?可以使用dfs。可是若用裸dfs,则程序会超时。我们可以考虑用并查集将图中不会被删除的边进行缩点,这样就可以大大减少程序运行的时间。

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

struct Node{
	int nxt,data;
};

int head[5010];
Node node[400010];
int k[200001][3];
int can[5010][5010]; 
bool lkan[5100];
int qus[10001][3];
bool vis[5010];
int n,m,ans,cnt=1,tot=0;
int fa[5010];
bool kan[5010][5010];

void add(int x,int y){
	node[cnt].nxt=head[x];node[cnt].data=y;head[x]=cnt;cnt++;
	node[cnt].nxt=head[y];node[cnt].data=x;head[y]=cnt;cnt++; 
}

inline int find(int x){
	int tmp=x,nxt;
	while(tmp!=fa[tmp])tmp=fa[tmp];
	while(x!=tmp){
		nxt=fa[x];
		fa[x]=tmp;
		x=nxt;
	}
	return tmp;
}

inline void merge(int x,int y){
	int fx=find(x),fy=find(y);
	if(fx!=fy)fa[fx]=fy;
}

inline int in(){
	int ans=0;
	char x=getchar();
	while(x<'0'||x>'9')x=getchar();
	while(x>='0'&&x<='9'){ans=ans*10+x-'0';x=getchar();}
	return ans;
}

bool dfs(int now,int r){
	if(now==r)return true;
	for(int i=head[now];i;i=node[i].nxt){
		if(!vis[node[i].data]&&can[node[i].data][now]){
			vis[node[i].data]=true;
			if(node[i].data==r){
				return true;
			}
			if(dfs(node[i].data,r))return true;
		}
	}
	return false;
}

int main(){
	n=in();m=in();
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=1;i<=m;i++){
		int x,y;
		x=in();y=in();
		k[i][1]=x;
		k[i][2]=y;
		merge(x,y);	
	}
	
	for(int i=1;i<=n;i++){
		int x=find(i);
		if(!lkan[x]){
			lkan[x]=true;
			ans++;
		}
	}

	int q;
	q=in();
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=1;i<=q;i++){
		char c;
		scanf("%s",&c);
		if(c=='Q')qus[i][0]=-1;
		else if(c=='D'){
			int x,y;
			x=in();y=in();
			qus[i][0]=1;qus[i][1]=x;qus[i][2]=y;
			kan[x][y]=kan[y][x]=true;
		}
		else{
			int x,y;
			x=in();y=in();
			qus[i][0]=2;qus[i][1]=x;qus[i][2]=y;
		}
	}
	for(int i=1;i<=m;i++){
		if(!kan[k[i][1]][k[i][2]]){
			merge(k[i][1],k[i][2]);
		}
	}
	for(int i=1;i<=m;i++){
		int fx=find(k[i][1]),fy=find(k[i][2]);
		can[fx][fy]++;can[fy][fx]++;
		if(can[fx][fy]==1)
			add(find(fx),find(fy));
	}
	for(int i=1;i<=q;i++){
		int x=qus[i][1],y=qus[i][2];
		int fx=find(x),fy=find(y);
		if(qus[i][0]==-1)printf("%d\n",ans);
		else if(qus[i][0]==1){
			can[fx][fy]--;can[fy][fx]--;
			memset(vis,0,sizeof vis);
			vis[fx]=true;
			if(!dfs(fx,fy)){
				ans++;
			}
		}
		else{
			if(fx==fy)continue;
			memset(vis,0,sizeof vis);
			vis[fx]=true;
			if(!dfs(fx,fy)){
				ans--; 
			}
			can[fx][fy]++;can[fy][fx]++;
			if(can[fx][fy]==1)add(fx,fy);
		}
	}
	return 0;
}



你可能感兴趣的:(编程)