ZOJ - 3261 Connections in Galaxy War 并查集 离线操作

题意:在一个星系中,存在n个星球,每个星球都有一定的力量值,一些星球之间有建立通道方便在有需要的时候向比自己强的星球寻求帮助,也就是力量值比寻求帮助的星球要大。在星球大战中,给出2种类型的命令:

1.query a  表示查询比a强的中最强的星球编号,如果最强编号有多个与a相连,选择编号小的那个。

2.destroy a b 表示在大战中a和b之间的通道被毁,a b断开联系。

先给出星球个数n,然后是n个星球的力量值,然后给出m,表示m条边,最后是Q个询问。

并查集可以合并,但是不能断开。所以逆向操作,一开始就处理所有的destory命令,直接到最后的状态,从后往前处理所有的命令,当遇到destory命令时在将边的两点加入集合。合并的规格是优先力量值大的星球编号为根,力量值相等优先编号小的为根。这样查询时用Find函数就可以直接找到。

询问要存储起来,边总是规定左边小,方便标记。

#include<cstdio>
#include<map>
#include<algorithm>
using namespace std;
map<pair<int,int>,bool> mp;
const int maxn = 10010;//星球个数 
const int maxq = 50010;
int p[maxn],ans[5*maxn];
int f[maxn];
struct query
{
	char d[20];
	int a,b;
}q[maxq];
struct edge
{
	int u,v;
}e[2*maxn];
int Find(int x)
{
	return f[x]==x?x:f[x]=Find(f[x]);
}
void merge(int x,int y)
{
	int rtx=Find(x);
	int rty=Find(y);//根结点 
	if(rtx!=rty)
	{
		if(p[rtx]>p[rty])
		f[rty]=rtx;
		else if(p[rtx]<p[rty])
		f[rtx]=rty;
		else	//相等按id小的 
		{
			if(rtx<rty)
			f[rty]=rtx;
			else
			f[rtx]=rty;
		}
	}
}
int main()
{
	int n,fir=1;
//	freopen("in.txt","r",stdin);
	while(~scanf("%d",&n))
	{
		mp.clear(); 
		for(int i=0;i<n;i++)f[i]=i;
		for(int i=0;i<n;i++)scanf("%d",&p[i]);
		int m;
		scanf("%d",&m);
		int x,y;
		for(int i=0;i<m;i++)
		{
			scanf("%d%d",&x,&y);
			pair<int,int> temp;
			if(x>y)swap(x,y);//总是使得编号小的在右边 
			e[i].u=x;e[i].v=y;
			temp=make_pair(x,y);
			mp[temp]=0;
		}
		int Q;
		scanf("%d",&Q);
		for(int i=0;i<Q;i++)
		{
			scanf("%s",q[i].d);
			if(q[i].d[0]=='q')
			{
				scanf("%d",&q[i].a);
			}
			else
			{
				pair<int,int> temp;
				int u,v;
				scanf("%d%d",&u,&v);
				if(u>v)swap(u,v);	//仍然使之左边小 方便记录  * 
				q[i].a=u;q[i].b=v;
				temp=make_pair(q[i].a,q[i].b);
				mp[temp]=1;//这条边被毁坏 
			}
		}
		for(int i=0;i<m;i++)
		{
			pair<int,int> temp;
			temp=make_pair(e[i].u,e[i].v);
			if(!mp[temp])
			merge(e[i].u,e[i].v);
		}
		//离线
		int k=0;
		for(int i=Q-1;i>=0;i--)
		{
			if(q[i].d[0]=='q')
			{
				int pos=Find(q[i].a);
				if(p[pos]>p[q[i].a])
				ans[k++]=pos;
				else
				ans[k++]=-1;
			}
			else if(q[i].d[0]=='d')
			merge(q[i].a,q[i].b);
		}
		if(fir)fir=0;
		else printf("\n");
		for(int i=k-1;i>=0;i--)
		printf("%d\n",ans[i]);
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值