kuangbin带你飞 专题五 并查集
kuangbin带你飞 专题五 并查集
- kuangbin带你飞 专题五 并查集
- POJ 2236 Wireless Network
- POJ 1611 The Suspects
- HDU 1213 How Many Tables
- HDU 3038 How Many Answers Are Wrong
- POJ 1182 食物链
- POJ 1417 True Liars
- POJ 1456 Supermarket
- POJ 1733 Parity game
- POJ 1984 Navigation Nightmare
- POJ 2492 A Bug's Life
- POJ 2912 Rochambeau
- ZOJ 3261 Connections in Galaxy War
- HDU 1272 小希的迷宫
- POJ 1308 Is It A Tree?
POJ 2236 Wireless Network
大意:
给出n个电脑的坐标,一开始都是坏的,现在有两个操作,一个是修理坏的电脑,一个是测试修好的电脑的连通性
距离是d以内的电脑可以直接通信,距离大于d的也可以通过可以直接通信的电脑间接通信
要求输出每次测试的结果(能否通信)
思路:
维护一个已经修好的电脑的数组,每次修好一个新电脑都加到这个数组里,并且遍历这个数组,判断能否联通,能联通的话就合并并查集
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <math.h>
#include <cstdio>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int n;
double d;
struct node {
double x, y;
} a[N];
int f[N];
int findf(int x) { return x == f[x] ? x : findf(f[x]); }
void Union(int x, int y) {
int fx = findf(x);
int fy = findf(y);
if (fx != fy) {
f[fy] = fx;
}
}
int nice[N], num = 0;
int main() {
cin >> n >> d;
for (int i = 1; i <= n; i++) {
cin >> a[i].x >> a[i].y;
f[i] = i;
}
string op;
while (cin >> op) {
if (op == "S") {
int x, y;
cin >> x >> y;
if (findf(x) == findf(y))
cout << "SUCCESS" << endl;
else
cout << "FAIL" << endl;
} else {
int x;
cin >> x;
nice[num++] = x;
for (int j = 0; j < num; j++) {
if ((a[x].x - a[nice[j]].x) * (a[x].x - a[nice[j]].x) +
(a[x].y - a[nice[j]].y) * (a[x].y - a[nice[j]].y) <=
d * d)
Union(x, nice[j]);
}
}
}
return 0;
}
POJ 1611 The Suspects
大意:
n个学生,标号为0到n-1
m个社团,同一个社团的学生中如果有需要被隔离的学生,那么这个社团中所有的人都需要被隔离
0号学生是需要被隔离的,问一共需要隔离多少人
思路:
直接并查集模拟就可以
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int f[N];
int findf(int x) { return x == f[x] ? x : findf(f[x]); }
void Union(int x, int y) {
int fx = findf(x);
int fy = findf(y);
if (fx != fy) {
f[fy] = fx;
}
}
int n, m;
int is[N], num = 0, tmp[N];
int main() {
while (cin >> n >> m) {
if (n + m == 0) break;
for (int i = 0; i < n; i++) f[i] = i;
for (int i = 0; i < m; i++) {
int k;
cin >> k;
for (int i = 0; i < k; i++) {
cin >> tmp[i];
if (i) Union(tmp[i - 1], tmp[i]);
}
}
int res = 0;
for (int i = 0; i < n;i++){
if (findf(0) == findf(i)) res++;
}
cout << res << endl;
}
return 0;
}
HDU 1213 How Many Tables
大意:
n个人,m对朋友,有朋友关系(直接或间接)的可以坐一桌,问需要多少个桌子
思路:
简单并查集
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <set>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int f[N];
int findf(int x) { return x == f[x] ? x : findf(f[x]); }
void Union(int x, int y) {
int fx = findf(x);
int fy = findf(y);
if (fx != fy) {
f[fy] = fx;
}
}
int n, m;
int main() {
int t;
cin >> t;
while (t--) {
cin >> n >> m;
for (int i = 1; i <= n; i++) f[i] = i;
for (int i = 0; i < m; i++) {
int x, y;
cin >> x >> y;
Union(x, y);
}
set<int> s;
for (int i = 1; i <= n;i++){
s.insert(findf(i));
}
cout << s.size() << endl;
}
return 0;
}
HDU 3038 How Many Answers Are Wrong
大意:
n个数的数组,现在给出m个描述,x y z代表区间x到y的区间和为z
问这些表述中有哪些是错误的
注意有多组输入,但是题目好像没说.....
思路:
区间x y的和为z,也就是说第y个数比第x-1个数大z,那么可以利用带权并查集,维护点到父节点的距离(也就是比父节点大多少)
然后如果输入的两个点是同一个父节点,那么就看两个点到父节点距离的差是否等于z
如果不是一个父节点,那么就要合并父节点
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <set>
#include <stack>
#include <vector>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int n, m;
int f[N], d[N];
int findf(int x) {
if (x == f[x]) return x;
int tmp = findf(f[x]);
d[x] += d[f[x]];
return f[x] = tmp;
}
void Union(int x, int y, int w) {
int fx = findf(x);
int fy = findf(y);
if (fx != fy) {
f[fy] = fx;
d[fy] = w - d[y] + d[x];
}
}
int res = 0;
int main() {
while (cin >> n >> m) {
res = 0;
for (int i = 0; i <= n; i++) {
f[i] = i;
d[i] = 0;
}
while (m--) {
int x, y, z;
cin >> x >> y >> z;
x--;
int fx = findf(x), fy = findf(y);
if (fx != fy) {
Union(x, y, z);
} else {
if (d[y] - d[x] != z) res++;
}
}
cout << res << endl;
}
return 0;
}
POJ 1182 食物链
大意:
有三类动物A,B,C。A吃B, B吃C,C吃A。
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是"1 X Y",表示X和Y是同类。
第二种说法是"2 X Y",表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中X或Y比N大,就是假话;
3) 当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。
思路:
因为只有三个类型,所以直接可以利用3*n的拓展域并查集,i代表自身种类所在的集合,i+n代表猎物所在集合,i+2n代表天敌所在集合
如果是说法1,那么就将x和y x+n和y+n x+2n和y+2n合并
如果是说法2,那么就将x和y+2n合并,x+n和y合并,x+2n和y+n合并
注意必须是单组输入....
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <set>
#include <stack>
#include <vector>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int n, m;
int f[N], d[N];
int findf(int x) { return x == f[x] ? x : findf(f[x]); }
void Union(int x, int y) {
int fx = findf(x);
int fy = findf(y);
if (fx != fy) {
f[fy] = fx;
}
}
int res = 0;
int main() {
scanf("%d%d",&n,&m);
res = 0;
for (int i = 1; i <= 3 * n; i++) {
f[i] = i;
}
while (m--) {
int d, x, y;
scanf("%d%d%d",&d,&x,&y);
if (x > n || y > n) {
res++;
continue;
}
if (d == 1) {
int fx = findf(x + n), fy = findf(y);
if (fx == fy) {
res++;
continue;
}
fx = findf(x + 2 * n), fy = findf(y);
if (fx == fy) {
res++;
continue;
}
Union(x, y);
Union(x + n, y + n); //猎物
Union(x + 2 * n, y + 2 * n); //天敌
} else {
if (x == y) {
res++;
continue;
}
int fx = findf(x), fy = findf(y);
if (fx == fy) { //吃同类
res++;
continue;
}
fx = findf(x + 2 * n), fy = findf(y);
if (fx == fy) { //吃天敌
res++;
continue;
}
Union(x + n, y);
Union(x, y + 2 * n);
Union(x + 2 * n, y + n);
}
}
printf("%d\n",res);
return 0;
}
POJ 1417 True Liars
大意:
现在有p1个好人 p2个坏人,好人一定说真话,坏人一定说假话
现在给出n句话,格式是x y z,如果z=1,那么代表x说y是好人 ,否则代表x说y是坏人
问能否根据这n句话判断出哪些是好人,能的话从小到大输出哪些是好人
思路:
很容易推出,如果x说y是好人,那么x和y必然同类,否则必然不同类
权值并查集,权值为0代表和父节点同类,1代表和父节点不同类,然后用异或维护即可
但是需要判断能否推出哪些人是好人,由于最后形成的是很多联通块,所以无法直接判断,这就需要利用01背包了
一个物品有两个属性,可以选任意一个属性,最后看能否组成1即可,具体看代码
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
using namespace std;
const int N = 1e3 + 5;
typedef long long LL;
int f[N], d[N];
int findf(int x) {
if (x == f[x]) return x;
int fx = findf(f[x]);
d[x] ^= d[f[x]];
return f[x] = fx;
}
void Union(int x, int y, int diff) {
int fx = findf(x);
int fy = findf(y);
if (fx != fy) {
f[fy] = fx;
d[fy] = d[x] ^ d[y] ^ diff;
}
}
int n, m, p1, p2, vis[N], num[N][2], dp[N][N];
vector<int> a[N][2], res;
int main() {
while (scanf("%d%d%d", &n, &p1, &p2) && (n + p1 + p2 != 0)) {
for (int i = 1; i <= 2 * (p1 + p2); i++) {
f[i] = i;
a[i][0].clear();
a[i][1].clear();
num[i][0] = num[i][1] = 0;
vis[i] = 0;
d[i] = 0;
}
res.clear();
while (n--) {
int x, y;
char s[5];
scanf("%d%d%s", &x, &y, &s);
if (s[0] == 'y') {
Union(x, y, 0);
} else {
Union(x, y, 1);
}
}
int cnt = 0;
for (int i = 1; i <= (p1 + p2); i++) {
if (vis[i]) continue;
int fi = findf(i);
cnt++;
for (int j = i; j <= (p1 + p2); j++) {
int fj = findf(j);
if (fj == fi) {
num[cnt][d[j]]++;
a[cnt][d[j]].push_back(j);
vis[j] = 1;
}
}
}
memset(dp, 0, sizeof dp);
dp[0][0] = 1;
for (int i = 1; i <= cnt; i++) {
for (int j = p1; j >= min(num[i][0], num[i][1]); j--) {
if (j >= num[i][0]) dp[i][j] += dp[i-1][j - num[i][0]];
if (j >= num[i][1]) dp[i][j] += dp[i-1][j - num[i][1]];
}
}
if (dp[cnt][p1] != 1) {
cout << "no" << endl;
continue;
}
for (int i = cnt; i >= 1; i--) {
if (p1 - num[i][0] >= 0 && p2 - num[i][1] >= 0 &&
dp[i-1][p1 - num[i][0]] == 1) {
p1 -= num[i][0];
p2 -= num[i][1];
for (int j = 0; j < a[i][0].size(); j++)
res.push_back(a[i][0][j]);
} else if (p1 - num[i][1] >= 0 && p2 - num[i][0] >= 0 &&
dp[i-1][p1 - num[i][1]] == 1) {
p1 -= num[i][1];
p2 -= num[i][0];
for (int j = 0; j < a[i][1].size(); j++)
res.push_back(a[i][1][j]);
}
}
sort(res.begin(), res.end());
for (int i = 0; i < res.size(); i++) {
cout << res[i] << endl;
}
cout << "end" << endl;
}
return 0;
}
POJ 1456 Supermarket
大意:
N个商品,每个商品都有利润p和过期时间d
每天只能卖一个商品,问怎么安排可以收益最大,输出最大收益
思路:
贪心的想,只要在过期时间之前,那么越往后卖越好,这样可以给早过期的商品时间
所以可以利用并查集,维护已经被占用的时间,天数i的父节点fi代表上一个能卖的时间,每次查询\(f[d]\),看是否大于0,如果大于0就和\(f[d]-1\)合并
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <queue>
#include <set>
#include <stack>
#include <vector>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int n, m;
int f[N];
int findf(int x) { return x == f[x] ? x : findf(f[x]); }
void Union(int x, int y) {
int fx = findf(x);
int fy = findf(y);
if (fx != fy) {
f[fy] = fx;
}
}
int res = 0;
struct node {
int p, d;
} a[N];
bool cmp(node a, node b) { return a.p > b.p; }
int main() {
while (scanf("%d", &n) != EOF) {
for (int i = 0; i < n; i++) {
cin >> a[i].p >> a[i].d;
}
for (int i = 0; i <= 10000; i++) {
f[i] = i;
}
res = 0;
sort(a, a + n, cmp);
for (int i = 0; i < n; i++) {
int fy = findf(a[i].d);
if(fy>0){
Union(fy-1, fy);
res += a[i].p;
}
}
printf("%d\n", res);
}
return 0;
}
POJ 1733 Parity game
大意:
现在有一个长度为n的01序列,给出m个说法,代表l到r之间有奇数个还是偶数个1,为前几个说法是自洽的
n的范围为1e9 m的范围为5e5
思路:
带权并查集,用异或维护与父节点的奇偶性关系,如果输入为l到r有偶数个1,那么代表r与l-1的奇偶性相同,否则是不同
因为n很大,所以需要离散化
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
using namespace std;
const int N = 5e3 + 5;
typedef long long LL;
int f[N], d[N];
int findf(int x) {
if (x == f[x]) return x;
int fx = findf(f[x]);
d[x] ^= d[f[x]];
return f[x] = fx;
}
void Union(int x, int y, int diff) {
int fx = findf(x);
int fy = findf(y);
if (fx != fy) {
f[fy] = fx;
d[fy] = d[x] ^ d[y] ^ diff;
}
}
int n, m, t = 0, a[2 * N];
struct node {
int l, r, ans;
} query[N];
void read_discrete() {
for (int i = 1; i <= m; ++i) {
char str[5];
scanf("%d%d%s", &query[i].l, &query[i].r, str);
query[i].ans = (str[0] == 'o' ? 1 : 0);
a[++t] = query[i].l - 1;
a[++t] = query[i].r;
}
sort(a + 1, a + 1 + t);
n = unique(a + 1, a + 1 + t) - a - 1; // n <= m
}
int get(int num) { return lower_bound(a + 1, a + n + 1, num) - a; }
int main() {
cin >> n;
cin >> m;
read_discrete();
for (int i = 0; i <= n; i++) f[i] = i;
for (int i = 1; i <= m; i++) {
int x = get(query[i].l - 1);
int y = get(query[i].r);
int fx = findf(x), fy = findf(y);
if (fx == fy) {
if (d[x] ^ d[y] != query[i].ans) {
cout << i-1 << endl;
return 0;
}
} else {
Union(x, y, query[i].ans);
}
}
cout << m << endl;
return 0;
}
POJ 1984 Navigation Nightmare
大意:
有n个农场,m条路,每条路都连接两个农场,且为纵向或者横向的,每条路的格式为x y d f,代表x在y的f方向,这条路长度为d
现在有q个询问,每个询问的格式为x y t,即只看前t条路的情况下,x和y的曼哈顿距离是多少 不相连的话输出-1
思路:
因为要输出曼哈顿距离,所以不能直接维护一个d数组,应该维护一个横向的数组d1和纵向的数组d2,同时更新这两个数组即可
注意输入时t不是递增的,需要排序,相应的答案也要先存起来,最后依次输出
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
using namespace std;
const int N = 5e5 + 5;
typedef long long LL;
int f[N], d1[N],d2[N];
int findf(int x) {
if (x == f[x]) return x;
int fx = findf(f[x]);
d1[x] += d1[f[x]];
d2[x] += d2[f[x]];
return f[x] = fx;
}
void Union(int x, int y, int diff1,int diff2) {
int fx = findf(x);
int fy = findf(y);
if (fx != fy) {
f[fy] = fx;
d1[fy] = d1[x] - d1[y] + diff1;
d2[fy] = d2[x] - d2[y] + diff2;
}
}
int n, m, t = 0, a[2 * N],q;
int res[N];
struct node {
int x, y, ans;
char f;
int id;
} query1[N], query2[N];
bool cmp(node a, node b) { return a.ans < b.ans; }
int main() {
scanf("%d", &n);
scanf("%d", &m);
for (int i = 0; i <= n; i++) f[i] = i;
for (int i = 1; i <= m; i++) {
scanf("%d %d %d %c", &query1[i].x, &query1[i].y, &query1[i].ans, &query1[i].f);
}
scanf("%d", &q);
for (int i = 0; i < q; i++) {
scanf("%d %d %d", &query2[i].x, &query2[i].y, &query2[i].ans);
query2[i].id = i;
}
sort(query2, query2 + q, cmp);
int now = 1;
for (int i = 0; i < q; i++) {
for (int j = now; j <= query2[i].ans; j++) {
int x = query1[j].x, y = query1[j].y, dis = query1[j].ans;
if (query1[j].f == 'E') Union(x, y, -dis, 0);
if (query1[j].f == 'W') Union(x, y, dis, 0);
if (query1[j].f == 'N') Union(x, y, 0, -dis);
if (query1[j].f == 'S') Union(x, y, 0, dis);
}
now = query2[i].ans;
int x = query2[i].x, y = query2[i].y;
int fx = findf(x), fy = findf(y);
if (fx != fy) {
res[query2[i].id] = -1;
} else {
res[query2[i].id] = abs(d1[x] - d1[y]) + abs(d2[x] - d2[y]);
}
}
for (int i = 0; i < q;i++){
printf("%d\n", res[i]);
}
return 0;
}
POJ 2492 A Bug's Life
大意:
给出n个小虫,m个关系,每个关系代表两个小虫可以交配,问有没有异常的小虫(同性交配)
思路:
拓展域并查集裸题
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
using namespace std;
const int N = 2e3 + 5;
typedef long long LL;
int f[N], d[N];
int findf(int x) { return x == f[x] ? x : f[x] = findf(f[x]); }
void Union(int x, int y) {
int fx = findf(x);
int fy = findf(y);
if (fx != fy) {
f[fy] = fx;
}
}
int n, m, t, cases = 0;
vector<int> a[N][2], res;
int main() {
cin >> t;
while (t--) {
scanf("%d%d", &n, &m);
cases++;
if (cases != 1) printf("\n");
int flag = 0;
for (int i = 1; i <= 2 * n; i++) f[i] = i;
for (int i = 1; i <= m; i++) {
int x, y;
scanf("%d%d", &x, &y);
int fx = findf(x), fy = findf(y);
if (fx == fy) {
flag = 1;
} else {
Union(x, y + n);
Union(x + n, y);
}
}
if (flag)
printf("Scenario #%d:\nSuspicious bugs found!\n", cases);
else
printf("Scenario #%d:\nNo suspicious bugs found!\n", cases);
}
return 0;
}
POJ 2912 Rochambeau
大意:
给出n个人,进行剪子包袱锤,给出m次比赛的结果,每个人出的手势是固定的,但是有一个裁判可以随意出,问能否找到这个裁判,并输出在第几次比赛结果后可以判定这个裁判
如果不能找到裁判,那么输出Impossible,如果不能确定哪个是裁判,那么输出Can not determine
n<=500
思路:
枚举裁判,然后判断它是否能成为裁判,即忽略当前枚举的这个人,然后用并查集判断有没有冲突,如果有冲突,那么这个人不是裁判,记录从冲突的行数,然后break(这里写了continue,wa了好久)
如果存在一个可以成为裁判的人(也就是没有冲突)那么输出这个人,而且确定他为裁判的位置是其他所有冲突的行的最大的位置(因为要确定其他的都不是才能确定这个人是裁判)
如果存在两个或以上的人可以成为裁判,那么输出Can not determine
如果一个都没有,那么输出Impossible
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
using namespace std;
const int N = 2e4 + 5;
typedef long long LL;
int f[N], d[N];
int findf(int x) { return x == f[x] ? x : f[x] = findf(f[x]); }
void Union(int x, int y) {
int fx = findf(x);
int fy = findf(y);
if (fx != fy) {
f[fy] = fx;
}
}
int n, m, t;
struct node {
int x, y;
char c;
} a[N];
int main() {
while (scanf("%d%d", &n, &m) != EOF) {
int flag = 0;
for (int i = 1; i <= m; i++) {
scanf("%d %c %d", &a[i].x, &a[i].c, &a[i].y);
}
int line = 0,cnt=0 , res=0;
for (int i = 0; i < n; i++) {
int flag = 0;
for (int j = 0; j < 3 * n; j++) f[j] = j;
for (int j = 1; j <= m; j++) {
int x = a[j].x, y = a[j].y;
char c = a[j].c;
if (x == i || y == i) continue;
if (c == '>') {
int fx = findf(x);
int fy = findf(y + n);
if(fx==fy){
flag = 1;
line = max(line, j);
break;
}
fx = findf(x);
fy = findf(y);
if(fx==fy){
flag = 1;
line = max(line, j);
break;
}
Union(x + n, y), Union(x, y + 2 * n), Union(x + 2 * n, y + n);
}
if (c == '=') {
int fx = findf(x);
int fy = findf(y + n);
if(fx==fy){
flag = 1;
line = max(line, j);
break;
}
fx = findf(x+n);
fy = findf(y);
if(fx==fy){
flag = 1;
line = max(line, j);
break;
}
Union(x, y), Union(x + 2 * n, y + 2 * n), Union(x + n, y + n);
}
if (c == '<') {
int fx = findf(x);
int fy = findf(y);
if(fx==fy){
flag = 1;
line = max(line, j);
break;
}
fx = findf(x + n);
fy = findf(y);
if(fx==fy){
flag = 1;
line = max(line, j);
break;
}
Union(x, y + n), Union(x + 2 * n, y), Union(x + n, y + 2 * n);
}
}
if (flag == 0) cnt++, res = i;
}
if(!cnt)printf("Impossible\n");
else if(cnt>1)printf("Can not determine\n");
else
printf("Player %d can be determined to be the judge after %d lines\n", res, line);
}
return 0;
}
ZOJ 3261 Connections in Galaxy War
大意:
给出n个星球,m条路,每个星球都需要向能联通的星球中,实力比他强且为联通块中最强的星球求援,最大实力相同则向下标最小的求援
给出q次查询,有两种操作,一种是破坏某一条路,另一种是问这个星球能求援的星球是哪个,没有符合要求的就输出-1
思路:
因为并查集适合处理合并,但是现在是分裂的操作,所以可以反向考虑,首先记录每条边的存活情况,然后将q次查询离线,先将最后仍然存活的边上的点联通,然后倒着查询,遇到一条被破坏的边就将其联通即可
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 5;
typedef long long LL;
int n;
int f[N], num[N], add[N], a[N], m, q;
map<pair<int, int>, bool> use;
int findf(int x) { return x == f[x] ? x : f[x] = findf(f[x]); }
void Union(int x, int y) {
int fx = findf(x);
int fy = findf(y);
if (fx != fy) {
f[fy] = fx;
if (num[fy] > num[fx])
num[fx] = num[fy], add[fx] = add[fy];
else if (num[fy] == num[fx] && add[fx] > add[fy])
add[fx] = add[fy];
}
}
struct node {
int op, x, y;
} query[5 * N];
struct node2 {
int u, v;
} edge[2 * N];
int res[5 * N];
int flag = 0;
int main() {
while (scanf("%d", &n) != EOF) {
if (!flag)
flag = 1;
else
printf("\n");
use.clear();
for (int i = 0; i < n; i++) {
f[i] = i;
scanf("%d", &a[i]);
num[i] = a[i];
add[i] = i;
}
scanf("%d", &m);
for (int i = 0; i < m; i++) {
int u, v;
scanf("%d%d", &u, &v);
if (u > v) swap(u, v);
use[{u, v}] = false;
edge[i].u = u, edge[i].v = v;
}
scanf("%d", &q);
for (int i = 0; i < q; i++) {
char s[10];
scanf("%s%d", &s, &query[i].x);
if (s[0] == 'q')
query[i].op = 1;
else {
scanf("%d", &query[i].y);
if (query[i].x > query[i].y) swap(query[i].x, query[i].y);
query[i].op = 0;
use[{query[i].x, query[i].y}] = true;
}
}
for (int i = 0; i < m; i++) {
int u = edge[i].u, v = edge[i].v;
if (!use[{u, v}]) Union(u, v);
}
for (int i = q - 1; i >= 0; i--) {
int op = query[i].op, x = query[i].x, y = query[i].y;
if (op) {
int fx = findf(x);
if (num[fx] > a[x])
res[i] = add[fx];
else
res[i] = -1;
} else
Union(x, y);
}
for (int i = 0; i < q; i++) {
if (query[i].op) cout << res[i] << endl;
}
}
return 0;
}
HDU 1272 小希的迷宫
大意:
判断一个图是否是联通图,且每个点之间只有一条路可达
思路:
并查集判断即可,比较坑的是子环和空树都是yes...
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
typedef long long LL;
int n;
int f[N], x, y;
map<pair<int, int>, bool> use;
int findf(int x) { return f[x] == x ? x : f[x] = findf(f[x]); }
void Union(int x, int y) {
int fx = findf(x);
int fy = findf(y);
if (fx != fy) {
f[fy] = fx;
}
}
int vis[N];
int main() {
while (scanf("%d%d", &x, &y) != EOF && (x != -1)) {
int flag = 1;
for (int i = 1; i <= 100000; i++) f[i] = i, vis[i] = 0;
if (x == 0) {
cout << "Yes" << endl;
continue;
}
vis[x] = vis[y] = 1;
Union(x, y);
while (scanf("%d%d", &x, &y) && (x + y != 0)) {
int fx = findf(x), fy = findf(y);
vis[x] = vis[y] = 1;
if (x == y) continue;
if (fx == fy)
flag = 0;
else
Union(x, y);
}
int sum = 0;
for (int i = 1; i <= 100000; i++)
if (vis[i] && findf(i) == i) sum++;
if (flag && sum == 1)
cout << "Yes" << endl;
else
cout << "No" << endl;
}
return 0;
}
POJ 1308 Is It A Tree?
大意:
和上题类似,不过是判断是否是树,所以不能出现自环、重复边
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
using namespace std;
const int N = 1e5 + 5;
typedef long long LL;
int n;
int f[N], x, y;
map<pair<int, int>, bool> use;
int findf(int x) { return f[x] == x ? x : f[x] = findf(f[x]); }
void Union(int x, int y) {
int fx = findf(x);
int fy = findf(y);
if (fx != fy) {
f[fy] = fx;
}
}
int vis[N];
int cases = 0;
int main() {
while (scanf("%d%d", &x, &y) != EOF && (x != -1)) {
cases++;
int flag = 1;
use.clear();
for (int i = 1; i <= 100000; i++) f[i] = i, vis[i] = 0;
if (x == 0) {
printf("Case %d is a tree.\n", cases);
continue;
}
vis[x] = vis[y] = 1;
pair<int, int> tmp;
tmp.first = x, tmp.second = y;
use[tmp] = 1;
if (x == y)
flag = 0;
else
Union(x, y);
while (scanf("%d%d", &x, &y) && (x + y != 0)) {
int fx = findf(x), fy = findf(y);
pair<int, int> tmp;
tmp.first = x, tmp.second = y;
if (use[tmp]) flag = 0;
vis[x] = vis[y] = 1;
use[tmp] = 1;
if (fx == fy)
flag = 0;
else
Union(x, y);
}
int sum = 0;
for (int i = 1; i <= 100000; i++)
if (vis[i] && findf(i) == i) sum++;
if (flag && sum == 1)
printf("Case %d is a tree.\n", cases);
else
printf("Case %d is not a tree.\n", cases);
}
return 0;
}