题意: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]) ;
}
}