1. 程式人生 > >●BZOJ 1854 [Scoi2010]遊戲

●BZOJ 1854 [Scoi2010]遊戲

最大 std 會有 fine can light mar http toolbar

題鏈:

http://www.lydsy.com/JudgeOnline/problem.php?id=1854

題解:

並查集(還可以用匈牙利算法進行單路增廣的二分圖匹配)

把每個武器看成是一條邊,每個傷害值看成是一個點,
那麽每一條邊就連接了兩個點。
並把一條邊e與其一個端點u的“對應”表示為用這個武器e打出傷害u。
對於一個聯通塊,我們考慮把點和邊一一對應,使得被對應的點盡量多。
1).對於一棵樹來說,就會有一個點沒有邊與之對應,令那個點為聯通塊裏編號最大的點。
2).而對於非樹圖來說,即存在環,那麽所有點都可以與一條邊對應。
這樣的話,就可以用並查集維護聯通信息了。做法如下:
對於輸入的 x,y,我們找到其各自所在聯通塊裏編號最大的點(也是根)fx,fy。
如果 fx==fy,那麽表明加入這條邊後這個聯通塊出現了環,則 vis[fx]=1;
否則(令 fx<fy),表明是兩個聯通塊合並了,則讓編號較小的那個(即fx)可以有邊與它對應,vis[fx]=1;
最後從左往右遍歷一遍 vis,找到第一個 i,使得 vis[i]=0,那麽答案即為 i-1。


代碼:

#include<cstdio>
#include<cstring>
#include<iostream>
#define MAXN 1050000
#define filein(x) freopen(#x".in","r",stdin);
#define fileout(x) freopen(#x".out","w",stdout);
using namespace std;
bool vis[MAXN];
int fa[MAXN];
int N;
int find(int x){
	return x==fa[x]?x:fa[x]=find(fa[x]);
}
void merge(int x,int y){
	if(x>y) swap(x,y);
	fa[x]=y; vis[x]=1;
}
int main()
{
	filein(game); fileout(game);
	scanf("%d",&N);
	for(int i=0;i<=1000000;i++) fa[i]=i;
	for(int i=1,x,y,fx,fy;i<=N;i++){
		scanf("%d%d",&x,&y);
		fx=find(x); fy=find(y);
		if(fx==fy) vis[fx]=1;
		else merge(fx,fy);
	}
	for(int i=1;i<=1000000;i++) if(!vis[i]){
		printf("%d",i-1); break;
	}
	return 0;
}

●BZOJ 1854 [Scoi2010]遊戲