zoj3261——(好题)并查集+离线(逆向)处理

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3261

n order to strengthen the defense ability, many stars in galaxy allied together and built many bidirectional tunnels to exchange messages. However, when the Galaxy War began, some tunnels were destroyed by the monsters from another dimension. Then many problems were raised when some of the stars wanted to seek help from the others.

In the galaxy, the stars are numbered from 0 to N-1 and their power was marked by a non-negative integer pi. When the star A wanted to seek help, it would send the message to the star with the largest power which was connected with star A directly or indirectly. In addition, this star should be more powerful than the star A. If there were more than one star which had the same largest power, then the one with the smallest serial number was chosen. And therefore, sometimes star A couldn't find such star for help.

Given the information of the war and the queries about some particular stars, for each query, please find out whether this star could seek another star for help and which star should be chosen.

 

Input

 

There are no more than 20 cases. Process to the end of file.

For each cases, the first line contains an integer N (1 <= N <= 10000), which is the number of stars. The second line contains N integers p0, p1, ... , pn-1 (0 <= pi <= 1000000000), representing the power of the i-th star. Then the third line is a single integer M (0 <= M <= 20000), that is the number of tunnels built before the war. Then M lines follows. Each line has two integers a, b (0 <= a, b <= N - 1, a != b), which means star a and star b has a connection tunnel. It's guaranteed that each connection will only be described once.

In the (M + 2)-th line is an integer Q (0 <= Q <= 50000) which is the number of the information and queries. In the following Q lines, each line will be written in one of next two formats.

"destroy a b" - the connection between star a and star b was destroyed by the monsters. It's guaranteed that the connection between star a and star b was available before the monsters' attack.

"query a" - star a wanted to know which star it should turn to for help

There is a blank line between consecutive cases.

 

Output

 

For each query in the input, if there is no star that star a can turn to for help, then output "-1"; otherwise, output the serial number of the chosen star.

Print a blank line between consecutive cases.

 

Sample Input

 

2
10 20
1
0 1
5
query 0
query 1
destroy 0 1
query 0
query 1

 

Sample Output

 

1
-1
-1
-1

题目翻译:

第二题:
假设有编号从0开始的n个点,每个点都有一个非负权值p[i]。现在有没有重边的m条边和Q个操作。
对于操作有两种类型
destroy a b 表示摧毁a,b点之间的边
query a 表示从a出发能到的点中,权值比a大权值最大,在权值最大前提下编号最小的点。如果没有这样的点输出-1。

Input

本题有多组数据(不超过20组),每组间会有一个空行,读到文件结束为止
第一行一个整数n,第二行n个非负整数表示点的权值
第三行一个整数m,之后m行两个整数a,b表示a,b之间有一条边。之后一个整数Q,之后Q行输入格式如题意。
1<=n<=10000,p[i]<=1000000000,0<=M<=20000,0<=Q<=50000)

Output

对于每个询问,输出相应的结果。两组测试数据之间输出一个空行

 

真的很好的题,这个题用到了一个处理并查集缺点的方法:离线存储,逆向处理。

并查集不能用来切掉某个边是我们众所周知的,因此我们这个题正面处理很麻烦。

根据正难则反的原则,我们可以反向来处理。
先存储下所有的操作,对所有没有被摧毁的边的节点进行合并。

之后反向对操作进行处理,碰到询问就判断根节点的权值是否大于当前值,大于则把根节点存下来,否则存 -1 ;

遇到摧毁操作,就把这个边的两个节点合并。

最后逆序遍历结果数组,输出就行了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
using namespace std;
const int maxn=1e4+7;
map<pair<int,int>,int> Hash;
//hash是用来标记是否边被破坏 
struct edge{//记录边的两个端点,以及操作的类型 
	int s,t,flag;
}query[maxn*5];
int num[maxn],w[maxn],p[maxn];
//num[i]表示从i到根节点的最大值 
int n,m,q;
void init(){//初始化父节点和最大值 
	for(int i=0;i<n;++i){
		p[i]=i;
		num[i]=w[i];
	}
}
int find(int x){//带权并查集经典做法(最大值) 
	int temp=p[x];
	if(p[x]!=x){
		p[x]=find(p[x]);//路径压缩 
		num[x]=max(num[x],num[temp]);//更新最大值 
	}
	return p[x];
}
void Union(int x,int y){//合并操作 
	int xx=find(x);
	int yy=find(y);
	if(xx==yy) return ;
	//如果x的根结点的权值大于y的根节点的权值
	//或者权值相同,但是编号小,符合题目给的要求
	//那么让xx为yy的父节点 
	if(num[xx]>num[yy]||(num[xx]==num[yy]&&xx<yy)) p[yy]=xx;
	else p[xx]=yy;
}
int main(){
	char opt[10];
	int first=1;
	while(~scanf("%d",&n)){
		if(first)//注意格式 
			first=0;
		else
			printf("\n");
		for(int i=0;i<n;++i)
			scanf("%d",&w[i]);
		Hash.clear();//清空 
		init();	
		scanf("%d",&m);
		for(int i=0;i<m;++i){
			int u,v;
			scanf("%d%d",&u,&v);
			if(u>v) swap(u,v);//不要忘记保证终点比起点大 ,后面同理 
			Hash[make_pair(u,v)]=1;//初始边都没被破坏 
		}
		scanf("%d",&q);
		for(int i=0;i<q;++i){//离线操作,预先存储下来,之后反向遍历 
			scanf("%s",opt);
			if(opt[0]=='q'){//0表示该操作是询问 同理1 
				scanf("%d",&query[i].s);
				query[i].flag=0;
			}
			else{//破坏操作 
				int u,v;
				scanf("%d%d",&u,&v);
				if(u>v) swap(u,v);
				query[i].s=u;
				query[i].t=v;
				query[i].flag=1;
				Hash[make_pair(u,v)]=0;//把被破坏的边标记清空 
			}
		}
		//遍历所有没有被破坏的边进行合并。 
		for(map<pair<int,int>,int>::iterator it=Hash.begin();it!=Hash.end();it++){
			if(it->second){
				pair<int,int> temp=it->first;
				Union(temp.first,temp.second);
			} 
		}
		vector<int> v;//用v来存储结果,开数组也行 
		v.clear();
		for(int i=q-1;i>=0;--i){//反向进行离线处理 
			if(query[i].flag)//如果该边被破怀过,现在合并,因为是倒着推的 
				Union(query[i].s,query[i].t);
			else{
				int x=query[i].s;
				int xx=find(x);
				//如果发现根节点的权值大于当前节点值,则能找到,否则不能 
				if(num[xx]<=w[x]) v.push_back(-1);
				else v.push_back(xx);
			}
		}
		int len=v.size();
		for(int i=len-1;i>=0;i--)//注意因为是反向跑的离线操作,所以反向输出结果 
			printf("%d\n",v[i]);
	}
	return 0;
}

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值