zoj 3261 反向并查集 点带权

题意:N个星球有M个无向边,每个星球有一个权值,在现有连边的基础上,询问与该点相连的最大权值点,这个最大权值点必须比自己的权值大,若有多个,则输出序号最小的。若没有输出-1。会有拆边操作,拆边后,在该边不通的基础上查询符合点。

题解反向并查集

1.这个比边带权的并查集简单,点带权注意题目要求就行。

2.因为并查集只能合并和查询,不能拆边,所以从上往下进行的拆边和查询交替的操作,可以等价于先把将要拆的边全部拆除,即最后一步查询时的图状态,然后反向进行添边和查询交替的操作,然后正向输出即可。

#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#define N 50005
using namespace std ;
int n , m , q ;
int o[N] , a[N] , b[N] , ans[N] ;
bool vis[N] ;
struct Edge
{
	int u , v ;
} edge[N] ;
struct Node
{
	int pre ;
	int power ;
} node[N] ;
int find(int x)
{
	int temp = node[x].pre ;
	if(x != temp)
	    node[x].pre = find(temp) ;
	return node[x].pre ;
}
void union1(int u , int v)
{
	int ru , rv ;
	ru = find(u) ;
	rv = find(v) ;
	if(ru != rv)
	{
		if(node[ru].power < node[rv].power)
		  node[ru].pre = rv ;
		else if(node[ru].power > node[rv].power)
		  node[rv].pre = ru ;
		else 
		  node[max(ru , rv)].pre = min(ru , rv) ;
	}
}
int main()
{
	int i , j ;
	bool flag = 0 ;
	char s[15] ;
	while(scanf("%d" , &n) != EOF)
	{
	  if(flag)
	     printf("\n") ;
	  flag = 1 ;
	  for(i = 0 ; i < n ; i ++)
	  {
	    node[i].pre = i ;
	    scanf("%d" , &node[i].power) ;	
	  }
	  scanf("%d" , &m) ;
	  for(i = 0 ; i < m ; i ++)
	      scanf("%d%d" , &edge[i].u , &edge[i].v) ;
	  memset(vis , 0 , sizeof(vis)) ;
	  scanf("%d" , &q) ;
	  for(i = 0 ; i < q ; i ++)
	  {
	  	scanf("%s" , s) ;
	  	if(s[0] == 'q')
	  	{
	  	   o[i] = 1 ;
	  	   scanf("%d" , &a[i]) ;	
		}
		else
		{
		   o[i] = 2 ;
	  	   scanf("%d%d" , &a[i] , &b[i]) ;	
	  	   for(j = 0 ; j < m ; j ++)
	  	       if(a[i] == edge[j].u && b[i] == edge[j].v || a[i] == edge[j].v && b[i] == edge[j].u)
               {
               	 vis[j] = 1 ;
               	 break ;
			   }
		}
	  }
	  for(i = 0 ; i < m ; i ++)
	      if(!vis[i])
	         union1(edge[i].u , edge[i].v) ;
	  memset(ans , -1 , sizeof(ans)) ; 
	  for(i = q - 1 ; i >= 0 ; i --)
	      if(o[i] == 1)
		  {
		    if(node[find(a[i])].power > node[a[i]].power)
			   ans[i] = find(a[i]) ; 
		  }
		  else
		  {
		  	union1(a[i] , b[i]) ;
		  } 
	   for(i = 0 ; i < q ; i ++)
	       if(o[i] == 1)
	          printf("%d\n" , ans[i]) ;
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值