您的位置:首页 > 其它

ZOJ 3261 Connections in Galaxy War 【并查集 + 离线逆向处理】好题!!

2018-03-02 16:24 585 查看
传送门

// 题意: 给定一幅(n, m) 图, 每个点有点权, 然后有一些询问

destroy a b 表示摧毁a与b直接相连的边, 保证删除之前这两个点之间一定有边

query a 询问a可以到达的点中点权最大的且比a大, 如果有多种可能输出编号较小的. 没有输出-1.

思路:这道题的还是一个比较中规中矩的并查集, 一看到删边,我们想要想到逆向处理, 即倒着回到询问, 然后遇到删边的表示这两个点之间连边, 因为并查集只有将两个集合并起来的功能, 而无法处理集合的拆离, 所以我们倒着处理. 具体处理方法就是先把询问先存下来, 然后把要除去要删除的边之外的其他边连接上, 然后倒着一个一个回答, 遇到删除的就在连上这两个点即可!! 询问就查该点的父亲看点权是否大于本身即可, 还有一点就是因为武力值相同要编号小的是ans, 所以我们要在Union上做一些处理, 那就是那武力值小的往大的上面并, 武力值相同的就让编号大的往小的上面并即可.

这道题最重要的收获点就是即把集合的拆离可以转换成集合之间的合并!!!

AC Code

const int maxn = 1e4+5;
int n, m;
int fa[maxn], _rank[maxn];
struct node{
int u, v;
}e[maxn<<1];
void init() {
for (int i = 0 ; i <= n ; i ++) {
fa[i] = i;
}
}
int Find(int x) {
return fa[x] == x ? x : fa[x] = Find(fa[x]);
}
void Un(int x, int y) {
int fx = Find(x);
int fy = Find(y);
if (fx != fy) {
if (_rank[fx] < _rank[fy]) fa[fx] = fy;
else if (_rank[fy] < _rank[fx]) fa[fy] = fx;
else {
if (fx < fy) swap(fx, fy);
fa[fx] = fy;
}
}
}
struct query {
string op;
int a, b;
}qq[maxn*5];
int ans[maxn*5];
void solve()
{
int cnt = 0;
while(~scanf("%d", &n)) {
if (cnt) printf("\n");
init(); map<pii, int>mp;
for (int i = 0 ; i < n ; i ++) {
scanf("%d", &_rank[i]);
}
scanf("%d", &m);
for (int i = 1 ; i <= m ; i ++) {
scanf("%d%d", &e[i].u, &e[i].v);
}
int q; scanf("%d", &q);
for (int i = 1 ; i <= q ; i ++) {
cin >> qq[i].op;
if (qq[i].op == "query") scanf("%d", &qq[i].a);
else {
scanf("%d%d", &qq[i].a, &qq[i].b);
mp[{qq[i].a, qq[i].b}] = mp[{qq[i].b, qq[i].a}] = 1;
}
}
for (int i = 1 ; i <= m ; i ++) {
int u = e[i].u, v = e[i].v;
if (mp[{u, v}]) continue;
Un(u, v);
}
int k = 0;
for (int i = q ; i >= 1 ; i --) {
if (qq[i].op == "query") {
int fu = Find(qq[i].a);
if (_rank[fu] <= _rank[qq[i].a]) ans[++k] = -1;
else ans[++k] = fu;
}
else Un(qq[i].a, qq[i].b);
}
for (int i = k ; i >= 1 ; i --) {
printf("%d\n", ans[i]);
}
cnt++;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: