题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3261
题目大意:先给你N个城市的能力, 然后给你M条路,再然后又Q次询问,如果为 query,输入 x,则询问与当前城市x相连的且比当前城市能力大的城市中能力最大的城市的编号,如果有多个最大城市,输出编号最小的,否则输出-1.如果为destory,输入x,y,则切断x,y这条路。
解题思路:如果正向查找的话,当切断一条路时没办法确定他们之间的关系了,因为查找时为路径压缩;
但是我们可以把询问存起来,把切断的路径标记一下,没标记的路径进行合并,注意合并的时候把能力大的城市当做根,如果能力一样,位置小的当做根,倒着遍历,如果遇见切断路径,就把它加进路径里面,接着就是并查集了。
做题心得:看到这道题不会做, 网上百度一下, 学习一下, 其实发现这道题没有那么困难, 难度也就是一般的并查集, 加点技巧就可以了,只是没有做过这种类型 的题,想不到方法。 不过看到大牛写的代码只后还是很佩服他们, 用了好多技巧, 代码中的唯一标记方法, 以前只是听说, 没见过,第一次见;还有我感觉特牛的是大牛竟然用map标记变,很方便, 如果想不到了, 最好方法就是vector标记了, 也不是很方便 啊, 所以这道题收货还是不小的、
</pre></p><p>代码如下:</p><p><pre name="code" class="cpp">#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<map>
using namespace std;
const int N = 10005;
const int M = 20005;
const int Q = 50005;
int f[N], ans[Q]; //f数组是并查集数组, ans数组存最后结果
//每个城市
struct node
{
int Id; //编号
int power;//能力大小
}p[N];
//城市的边
struct link
{
int x, y;
}q[M];
//存询问消息
struct que
{
char s[10];
int x;
int y;
}d[Q];
//用map标记变
map<int, bool> mp;
//并查集
int findset(int x)
{
return f[x] == x? x : f[x] = findset(f[x]);
}
//合并
void merge(int x, int y)
{
int rootx = findset(x);
int rooty = findset(y);
if(rootx != rooty) //根据题意要求进行合并
{
if(p[rootx].power > p[rooty].power)
f[rooty] = rootx;
else if(p[rootx].power < p[rooty].power)
f[rootx] = rooty;
else
{
if(p[rootx].Id > p[rooty].Id)
f[rootx] = rooty;
else
f[rooty] = rootx;
}
}
}
int main()
{
int n, m, t, flag = 1;
while(~scanf("%d", &n))
{
mp.clear(); //每次开始清空
for(int i = 0; i < n; i++)
{
scanf("%d", &p[i].power);
p[i].Id = i;
f[i] = i;
}
scanf("%d", &m);
for(int i = 0; i < m; i++)
{
scanf("%d%d", &q[i].x, &q[i].y);
//标记边 交换是因为边无向的(1,2)和(2,1)是同一条边 交换之后就可以使之更容易判断
if(q[i].x > q[i].y)
swap(q[i].x, q[i].y);
mp[q[i].x*10000+q[i].y] = false; //一个标记技巧, 使每条边的标记都是唯一的
}
//保存查询结果
scanf("%d", &t);
for(int i = 0; i < t; i++)
{
scanf("%s", d[i].s);
if(d[i].s[0] == 'q')
scanf("%d", &d[i].x);
else
{
scanf("%d%d", &d[i].x, &d[i].y);
if(d[i].x > d[i].y)
swap(d[i].x, d[i].y);
mp[d[i].x*10000+d[i].y] = true;//标记被毁坏的边
}
}
//把没有毁坏的边先合并起来
for(int i = 0; i < m; i++)
if(!mp[q[i].x*10000+q[i].y])
merge(q[i].x, q[i].y);
//逆向查找
int cnt = 0;
for(int i = t-1; i >= 0; i--)
if(d[i].s[0] == 'q')
{
int k = findset(d[i].x);
if(p[k].power > p[d[i].x].power)
ans[cnt++] = p[k].Id;
else
ans[cnt++] = -1;
}
else//如果边被毁坏,则毁坏之前肯定联通,就加进去
merge(d[i].x, d[i].y);
if(flag)
flag = 0;
else
printf("\n");
for(int i = cnt-1; i >= 0; i--)
printf("%d\n", ans[i]);
}
return 0;
}