1、poj 2236 Wireless Network
题意:一张图上分布着n台坏了的电脑,并知道它们的坐标。两台修好的电脑如果距离<=d就可以联网,也可以通过其他修好的电脑间接相连。给出操作“O x”表示修好x,给出操作“S x y”,请你判断x和y在此时有没有连接上。
思路:简单并查集。
1 #include<iostream> 2 #include<cstdio> 3 #include<memory.h> 4 #include<cmath> 5 using namespace std; 6 int n, d; 7 const int maxn = 1200; 8 int pre[maxn]; 9 struct node 10 { 11 int x; 12 int y; 13 }cpt[maxn]; 14 int Find(int x) 15 { 16 int r = x; 17 while (r != pre[r]) 18 { 19 r = pre[r]; 20 } 21 int i = x, j; 22 while (pre[i] != r) 23 { 24 j = pre[i]; 25 pre[i] = r; 26 i = j; 27 } 28 return r; 29 } 30 void Join(int x, int y) 31 { 32 int px = Find(x), py = Find(y); 33 if (px != py) pre[px] = py; 34 } 35 int main() 36 { 37 while (~scanf("%d%d", &n, &d)) 38 { 39 for (int i = 1; i <= n; i++) scanf("%d%d", &cpt[i].x, &cpt[i].y); 40 memset(pre, 0, sizeof(pre)); 41 char op[3]; 42 while (~scanf("%s", op)) 43 { 44 if (op[0] == 'O') 45 { 46 int p; 47 scanf("%d", &p); 48 pre[p] = p; 49 for (int i = 1; i <= n; i++) 50 { 51 if (i == p)continue; 52 double dis = sqrt(1.0*(cpt[i].x - cpt[p].x)*(cpt[i].x - cpt[p].x) + (cpt[i].y - cpt[p].y)*(cpt[i].y - cpt[p].y)); 53 if (dis <= d&&pre[i]!=0) 54 { 55 Join(i, p); 56 } 57 } 58 } 59 else 60 { 61 int p, q; 62 scanf("%d%d", &p, &q); 63 if (Find(p) != Find(q))printf("FAIL "); 64 else printf("SUCCESS "); 65 } 66 } 67 } 68 return 0; 69 }
2、poj 1611 The Suspects
题意:有一个学校,有N个学生,编号为0-N-1,现在0号学生感染了非典,凡是和0在一个社团的人就会感染,并且这些人如果还参加了别的社团,他所在的社团照样全部感染,求感染的人数。
思路:并查集,合并的时候顺便统计人数。
1 #include<iostream> 2 using namespace std; 3 const int maxn = 30010; 4 int pre[maxn]; 5 int Cnt[maxn]; 6 int Find(int x) 7 { 8 int r = x; 9 while (r != pre[r]) 10 { 11 r = pre[r]; 12 } 13 int i = x, j; 14 while (pre[i] != r) 15 { 16 j = pre[i]; 17 pre[i] = r; 18 i = j; 19 } 20 return r; 21 } 22 void Join(int x, int y) 23 { 24 int fx = Find(x), fy = Find(y); 25 if (fx != fy) pre[fx] = fy,Cnt[fy]+=Cnt[fx]; 26 } 27 int main() 28 { 29 int n, m; 30 while (~scanf("%d%d", &n, &m)) 31 { 32 for (int i = 0; i <= n; i++) pre[i] = i,Cnt[i]=1; 33 if (n == 0 && m == 0)break; 34 for (int i = 0; i < m; i++) 35 { 36 int k; 37 scanf("%d", &k); 38 int pre, now; 39 for (int i = 0; i < k; i++) 40 { 41 int x; 42 scanf("%d", &x); 43 if (i == 0) pre = now = x; 44 else 45 { 46 now = x; 47 Join(now, pre); 48 pre = now; 49 } 50 } 51 } 52 //int cnt = 0; 53 //for (int i = 0; i < n; i++) 54 //{ 55 // if (Find(i) == Find(0)) cnt++; 56 //} 57 printf("%d ", Cnt[Find(0)]); 58 } 59 return 0; 60 }
3、hdu 1213 How Many Tables
题意:两个人若互相认识,则坐在同一张桌子上。问需要多少张桌子。
思路:并查集,合并的时候更新剩余连通块。
1 #include<iostream> 2 using namespace std; 3 const int maxn = 1010; 4 int pre[maxn]; 5 int cnt; 6 int Find(int x) 7 { 8 int r = x; 9 while (r != pre[r]) 10 { 11 r = pre[r]; 12 } 13 int i = x, j; 14 while (pre[i] != r) 15 { 16 j = pre[i]; 17 pre[i] = r; 18 i = j; 19 } 20 return r; 21 } 22 void Join(int x, int y) 23 { 24 int fx = Find(x), fy = Find(y); 25 if (fx != fy) pre[fx] = fy, cnt--; 26 } 27 int main() 28 { 29 int t; 30 scanf("%d", &t); 31 while (t--) 32 { 33 int n, m; 34 scanf("%d%d", &n, &m); 35 cnt = n; 36 for (int i = 1; i <= n; i++) pre[i] = i; 37 for (int i = 0; i < m; i++) 38 { 39 int x, y; 40 scanf("%d%d", &x, &y); 41 Join(x, y); 42 } 43 printf("%d ", cnt); 44 } 45 return 0; 46 }
4、hdu 3038 How Many Answers Are Wrong
题意:给你M个区间和,问你有几个是与前面矛盾的。
思路:带权并查集,利用并查集形成集合树,利用sum数组记录集合树中当前节点i到根之间的边的权值和,如果当前两个节点不在同一棵集合树中,那么通过两个集合树合并,调整权值,使能够保证合法的,所以只需要合并,将一棵树的根变为另一棵树的根的子节点,然后建边,因为f[i]<i,i<j,f[j]<j,所以s=sum[j]+sum[f[j]]-sum[i]因为当前j的根是f[j],所以要加上新加的边权,那么新的边权既可以通过这个方程求取即可。
1 //利用并查集形成集合树,利用sum数组记录集合树中当前节点i到根之间的边的权值和,如果当前两个节点不在同一棵集合树中,那么通过两个集合树合并,调整权值,使能够保证合法的,所以只需要合并,将一棵树的根变为另一棵树的根的子节点,然后建边,因为f[i]<i,i<j,f[j]<j,所以s=sum[j]+sum[f[j]]-sum[i]因为当前j的根是f[j],所以要加上新加的边权,那么新的边权既可以通过这个方程求取即可 2 #include<stdio.h> 3 #define MAXN 200010 4 int f[MAXN], r[MAXN]; 5 int find(int x) 6 { 7 if (x == f[x]) 8 return f[x]; 9 int t = find(f[x]); 10 r[x] = r[x] + r[f[x]];//这里首先要对这个递归的过程很了解,当递归到某一层时,x还未接到根节点上,所以r[x]表示的是x到f[x]的距离,经上一步操作,f[x]已经接到根节点上了,所以r[f[x]]表示的是父节点到根节点的距离所以x到根节点的距离就直接等于r[x]+r[f[x]] 11 f[x] = t; 12 return f[x]; 13 } 14 int fun(int x, int y) 15 { 16 if (x>y) 17 return x - y; 18 else y - x; 19 } 20 int Union(int x, int y, int sum) 21 {//传进来时x<y 22 int a = find(x); 23 int b = find(y); 24 if (a == b) 25 { 26 if (fun(r[x], r[y]) == sum) 27 return 0; 28 else return 1; 29 } 30 else 31 { 32 f[a] = b; 33 r[a] = r[y] + sum - r[x];//r[y]表示y到b的距离,r[x]表示x到a的距离,sum表示x到y的距离,现在要将a接到b后面,那么r[a]很表示a到b的距离,很明显就是这个式子了 34 return 0; 35 } 36 } 37 int main() 38 { 39 int n, m, i, ans, a, b, s; 40 while (scanf("%d %d", &n, &m) != EOF) 41 { 42 ans = 0; 43 for (i = 0; i <= n; i++) 44 { 45 f[i] = i; 46 r[i] = 0; 47 } 48 for (i = 1; i <= m; i++) 49 { 50 scanf("%d %d %d", &a, &b, &s); 51 a--;//左区间减一,方便处理 52 if (Union(a, b, s)) 53 ans++; 54 } 55 printf("%d ", ans); 56 } 57 return 0; 58 }
5、poj 1182 食物链
题意:有 3 种动物,互相吃与被吃,现在告诉你 m 句话,其中有真有假,叫你判断假的个数 ( 如果前面没有与当前话冲突的,即认为其为真话 )。每句话开始都有三个数 D A B,当D = 1时,表示A 和B是同类,当D = 2时表示A 吃 B。
思路:带权并查集。
1 #include<iostream> 2 using namespace std; 3 const int maxn = 50010; 4 int n,k; 5 int pre[maxn], r[maxn]; 6 //根据“x与p[x]能否确定两者之间的关系”来划分,若能确定x与p[x]的关系,则它们同属一个集合。 7 //p[x]表示x的根结点。r[x]表示p[x]与x的关系。r[x] == 0 表示p[x]与x同类;1表示p[x]吃x;2表示x吃p[x]。 8 void Init() 9 { 10 for (int i = 0; i <= n; i++) 11 { 12 pre[i] = i; 13 r[i] = 0; 14 } 15 } 16 17 //每次通过儿子对父亲的关系,父亲对爷爷的关系,得到儿子对爷爷的关系 18 int Find(int x) 19 { 20 if (pre[x] == x) return x; 21 else 22 { 23 int fa = pre[x]; 24 pre[x] = Find(fa); 25 r[x] = (r[x] + r[fa]) % 3; 26 return pre[x]; 27 } 28 } 29 30 bool Union(int x, int y, int d) 31 {//d=1:x和y是同类;d=2,x吃y 32 int rx = Find(x), ry = Find(y); 33 if (rx == ry)return true; 34 else 35 { 36 pre[ry] = rx; 37 r[ry] = (r[x] - r[y] + 2 + d) % 3; 38 //r[ry]=(3-r[y]+(d-1)+r[x])%3(可推) 39 return false; 40 } 41 } 42 43 int main() 44 { 45 scanf("%d%d", &n, &k); 46 Init(); 47 int cnt = 0; 48 while (k--) 49 { 50 int d, x, y; 51 scanf("%d%d%d", &d, &x, &y); 52 if (x > n || y > n) cnt++; 53 else 54 { 55 bool same = Union(x, y, d); 56 if (same) 57 { 58 if (d == 1) 59 { 60 //D == 1 表示X与Y为同类,而从r[X] != r[Y]可以推出 X 与 Y 不同类。矛盾。 61 if (r[x] != r[y]) 62 { 63 cnt++; 64 } 65 } 66 else 67 { 68 if ((r[y] + 3 - r[x]) % 3 != 1) 69 {//(X 与Y为同类)或者 r[X] == ( r[Y] + 1 ) % 3 (Y吃X )则此话为假。 70 cnt++; 71 } 72 } 73 } 74 } 75 } 76 printf("%d ", cnt); 77 return 0; 78 }
6、poj 1417 True Liars(并查集+DP)
题意:给出p1+p2个人,其中p1个是好人,p2个是坏人。然后有一些关系 ,a说b是好人(坏人).其中没有矛盾的,判断是否有唯一解判断哪些人是好人,哪些人是坏人。其中比较重要的是,好人总说真话,坏人总说假话。
思路:
①一个人说另一个人是好人,那么如果这个人是好人,说明 对方确实是好人,如果这个是坏人,说明这句话是假的,对方也是坏人。
②如果一个人说另一个人是坏人,那么如果这个人是好人,说明对方是坏人,如果这个是坏人,说明 对方是好人。
也就是如果条件是yes说明这两个是相同集合的,否则是两个不同的集合。用r[i]表示i结点与根结点的关系,0为相同集合,1为不同集合。
通过并查集,可以将所有人分为若干个集合,其中对于每一个集合,又分为两个集合(好人和坏人,但是不知道哪些是好人,哪些是坏人,我们只有相对关系)
用dp[i][j]表示前i个集合中好人为j的种数,如果dp[cnt][p1]!=1说明方案不唯一,或者无解。belong[i][0]表示i属于哪个大类,belong[i][1]表示i属于该大类的哪一个小类,choose[i]倒推找出选择的大集合i的两类中是哪一类,然后升序输出好人的编号。
1 #include<iostream> 2 using namespace std; 3 int n, p1, p2; 4 const int maxn = 1100; 5 int pre[maxn];//i的父亲 6 int r[maxn];//i和pre[i]的关系 7 //0:同类;1:不同类 8 9 bool vis[maxn]; 10 11 int cntnum[maxn][2];//cntnum[i][j]表示把第i个集合分成两个部分的人数 12 13 int dp[maxn][310];//dp[i][j]表示前i个集合中好人为j的种数 14 15 int belong[maxn][2];//belong[i][0]表示i属于哪个大类,belong[i][1]表示i属于该大类的哪一个小类(对应cntnum) 16 int id[maxn]; 17 //根据孩子与父亲,父亲与祖父之间的关系可以推出孩子与祖父的关系 18 int Find(int x) 19 { 20 if (pre[x] == x) return x; 21 else 22 { 23 int fa = pre[x]; 24 pre[x] = Find(fa); 25 r[x] = r[x] ^ r[fa]; 26 return pre[x]; 27 } 28 } 29 30 void Union(int x, int y, int k) 31 { 32 int rx = Find(x), ry = Find(y); 33 if (rx != ry) 34 { 35 pre[rx] = ry; 36 r[rx] = (r[x] - r[y] + 2 + k) % 2; 37 } 38 } 39 40 void Init() 41 { 42 for (int i = 0; i <= p1 + p2; i++) 43 { 44 pre[i] = i; 45 r[i] = 0; 46 } 47 } 48 int main() 49 { 50 while (~scanf("%d%d%d", &n, &p1, &p2)) 51 { 52 if (n == 0 && p1 == 0 && p2 == 0) break; 53 Init(); 54 char s[5]; 55 int a, b, k; 56 for (int i = 0; i < n; i++) 57 { 58 scanf("%d%d%s", &a, &b, s); 59 if (s[0] == 'y') k = 0; 60 else k = 1; 61 Union(a, b, k); 62 } 63 //处理完后,得到若干个集合,每个集合中两两同类或不同类 64 //2:找到有多少个大集合,每个大集合中同类及不同类的个数 65 memset(vis, 0, sizeof(vis)); 66 memset(cntnum, 0, sizeof(cntnum)); 67 memset(belong, 0, sizeof(belong)); 68 69 int cnt = 0; 70 for (int i = 1; i <= p1 + p2; i++) 71 { 72 if (!vis[i]) 73 { 74 cnt++; 75 int f = Find(i); 76 id[f] = cnt;//对每个大集合的根进行分类 77 for (int j = i; j <= p1 + p2; j++) 78 { 79 if (Find(j) == f) 80 { 81 vis[j] = true; 82 cntnum[cnt][r[j]]++; 83 belong[j][0] = cnt; 84 belong[j][1] = r[j]; 85 } 86 } 87 } 88 } 89 //DP 90 memset(dp, 0, sizeof(dp)); 91 dp[0][0] = 1; 92 for (int i = 1; i <= cnt; i++) 93 { 94 for (int j = p1; j >= 0; j--) 95 { 96 if (j - cntnum[i][0] >= 0 && dp[i - 1][j - cntnum[i][0]] > 0) 97 { 98 dp[i][j] += dp[i - 1][j - cntnum[i][0]]; 99 } 100 if (j - cntnum[i][1] >= 0 && dp[i - 1][j - cntnum[i][1]] > 0) 101 { 102 dp[i][j] += dp[i - 1][j - cntnum[i][1]]; 103 } 104 } 105 } 106 107 //输出 108 if (dp[cnt][p1] != 1) printf("no "); 109 else 110 { 111 int choose[maxn];//倒推找出选择的大集合的两类中是哪一类 112 int totalpeople = p1; 113 for (int i = cnt; i >= 1; --i) 114 { 115 if (dp[i][totalpeople] == dp[i - 1][totalpeople - cntnum[i][0]]) 116 { 117 choose[i] = 0; 118 totalpeople -= cntnum[i][0]; 119 } 120 else if (dp[i][totalpeople] == dp[i - 1][totalpeople - cntnum[i][1]]) 121 { 122 choose[i] = 1; 123 totalpeople -= cntnum[i][1]; 124 } 125 } 126 127 for (int i = 1; i <= p1 + p2; i++) 128 { 129 int fa = Find(i); 130 int set_id = id[fa]; 131 if (belong[i][0] == set_id&&belong[i][1] == choose[set_id]) 132 { 133 printf("%d ", i); 134 } 135 } 136 printf("end "); 137 } 138 } 139 return 0; 140 }
7、poj 1456 Supermarket
题意:有N件商品,分别给出商品的价值和销售的最后期限,只要在最后日期之前销售处,就能得到相应的利润,并且销售该商品需要1天时间。问销售的最大利润。
思路:先把所有产品按照利润从大到小排序,假设一个产品a占用了一个日期后,那么如果下次又有一个产品b和产品a的截止日期是相同的,但是那个日期以被占用了,所以就要往前移动1天,那么就可以用并查集进行标记,在a占用了那个日期后,把a的截止日期指向前一个日期,这样的话,可以直接查找到他要占用到哪一个时间。
1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 int n; 5 const int maxn = 10010; 6 const int maxt = 10010; 7 struct node 8 { 9 int pi; 10 int di; 11 }goods[maxn]; 12 13 int pre[maxt]; 14 int Find(int x) 15 { 16 int r = x; 17 while (r != pre[r]) 18 { 19 r = pre[r]; 20 } 21 int i = x, j; 22 while (pre[i] != r) 23 { 24 j = pre[i]; 25 pre[i] = r; 26 i = j; 27 } 28 return r; 29 } 30 31 void Union(int x, int y) 32 { 33 int fx = Find(x), fy = Find(y); 34 if (fx != fy) 35 { 36 if (fx < fy) pre[fy] = fx; 37 else pre[fx] = fy; 38 } 39 } 40 41 42 void Init() 43 { 44 for (int i = 0; i <maxt; i++) 45 { 46 pre[i] = i; 47 } 48 } 49 bool Cmp(const node&a, const node&b) 50 { 51 if (a.pi == b.pi)return a.di > b.di; 52 else return a.pi > b.pi; 53 } 54 int main() 55 { 56 while (~scanf("%d", &n)) 57 { 58 Init(); 59 for (int i = 0; i < n; i++) 60 { 61 scanf("%d%d", &goods[i].pi, &goods[i].di); 62 } 63 sort(goods, goods + n, Cmp); 64 65 int sum = 0; 66 67 for (int i = 0; i < n; i++) 68 { 69 int tt = goods[i].di; 70 int ff = Find(tt); 71 if (ff > 0) 72 { 73 Union(ff, ff - 1); 74 sum += goods[i].pi; 75 } 76 } 77 printf("%d ", sum); 78 } 79 return 0; 80 }
8、poj 1703 Parity game
题意:有一个长度已知的01串,给出[l,r]这个区间中的1是奇数个还是偶数个,给出一系列语句问前几个是正确的。
思路:将[l,r]这个区间化为(l-1,r],那么1的个数就可以表示为sum[r]-sum[l-1],也就确定了奇偶性,我们可以用r[]数组表示这个端点到它的根节点的1的奇偶(这个区间就是(i,root(i)](0代表偶,1代表奇) 对于每个输入的区间,我们查找它们的根节点是否相同。
①如果相同,证明这个区间的奇偶性在之前已经得知,那么直接判断即可
②如果不同,那么就是u-1与v此时不在同一个集合中,则合并时更新r[].
1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 int n, m; 5 const int maxn = 5050; 6 int pre[maxn]; 7 int val[maxn]; 8 struct node 9 { 10 int ll; 11 int rr; 12 int d; 13 }qry[maxn]; 14 int Map[maxn * 2]; 15 int Find(int x) 16 { 17 if (x == pre[x]) return x; 18 else 19 { 20 int fa = pre[x]; 21 pre[x] = Find(fa); 22 val[x] = (val[x] + val[fa]) % 2; 23 return pre[x]; 24 } 25 } 26 bool Union(int x, int y, int v) 27 {//x<y 28 int rx = Find(x), ry = Find(y); 29 if (rx == ry) return true; 30 else 31 { 32 if (rx < ry) 33 { 34 pre[ry] = rx; 35 val[ry] = (val[x] + v - val[y] + 2) % 2; 36 } 37 else 38 { 39 pre[rx] = ry; 40 val[rx] = (val[y] - v - val[x] + 2) % 2; 41 } 42 return false; 43 } 44 } 45 int main() 46 { 47 while (~scanf("%d", &n)) 48 { 49 scanf("%d", &m); 50 int l, r, v; 51 char s[6]; 52 int cnt = 0; 53 for (int i = 0; i < m; i++) 54 { 55 scanf("%d%d%s", &l, &r, s); 56 l = l - 1; 57 if (s[0] == 'e') v = 0; 58 else v = 1; 59 qry[i].ll = l, qry[i].rr = r, qry[i].d = v; 60 Map[cnt++] = l; 61 Map[cnt++] = r; 62 } 63 //离散化 64 sort(Map, Map + cnt); 65 cnt = unique(Map, Map + cnt) - Map; 66 for (int i = 0; i <= cnt; i++) 67 { 68 pre[i] = i; 69 val[i] = 0; 70 } 71 int ans = 0; 72 for (int i = 0; i < m; i++) 73 { 74 l = lower_bound(Map, Map + cnt, qry[i].ll) - Map; 75 r = lower_bound(Map, Map + cnt, qry[i].rr) - Map; 76 v = qry[i].d; 77 if (Union(l, r, v)) 78 { 79 if ((val[r] - val[l] + 2) % 2 != v) 80 { 81 break; 82 } 83 84 } 85 ans++; 86 } 87 printf("%d ", ans); 88 } 89 return 0; 90 }
9、poj 1984 Navigation Nightmare
题意:图上有一些村庄,村庄之间用只能指向东南西北的方向的道路连接,村庄只能位于道路的两端。询问截止到第I个已知条件时某两个村庄之间的曼哈顿距离。
思路:先根据道路设出每个点的坐标,然后如果某个已知条件已知,则合并。询问时判断两个村庄是否在同一个连通块即可。
1 #include<iostream> 2 #include<algorithm> 3 #include<vector> 4 using namespace std; 5 int n, m,k; 6 const int maxn = 40050; 7 const int maxm = 40050; 8 const int maxk = 10050; 9 struct qry 10 { 11 int t; 12 int f1; 13 int f2; 14 int id; 15 }qrys[maxk]; 16 int ans[maxk]; 17 18 struct stp 19 { 20 int f1; 21 int f2; 22 stp(int ff1 = 0, int ff2 = 0) :f1(ff1), f2(ff2) 23 { 24 } 25 }; 26 vector<stp>stps; 27 struct pt 28 { 29 int x; 30 int y; 31 }points[maxn]; 32 bool Cmp1(const qry&a, const qry&b) 33 { 34 return a.t < b.t; 35 } 36 37 int pre[maxn]; 38 int Find(int x) 39 { 40 if (pre[x] == x) return x; 41 else 42 { 43 int fa = pre[x]; 44 pre[x] = Find(fa); 45 return pre[x]; 46 } 47 } 48 bool Union(int x, int y) 49 { 50 int fx = Find(x), fy = Find(y); 51 if (fx == fy) return true; 52 else 53 { 54 pre[fx] = fy; 55 return false; 56 } 57 } 58 struct node 59 { 60 int to; 61 char dir; 62 int len; 63 node(int tt=0,char c=0,int ll=0):to(tt),dir(c),len(ll){ } 64 }; 65 vector<node>mp[maxn]; 66 bool vis[maxn]; 67 void DFS(int r) 68 { 69 70 int sz = mp[r].size(); 71 for (int i = 0; i < sz; i++) 72 { 73 int tx = points[r].x, ty = points[r].y; 74 int v = mp[r][i].to, length = mp[r][i].len; 75 if (vis[v])continue; 76 switch (mp[r][i].dir) 77 { 78 case 'N':tx -= length; 79 break; 80 case 'E': ty += length; 81 break; 82 case 'S':tx += length; 83 break; 84 case 'W':ty -= length; 85 break; 86 } 87 points[v].x = tx, points[v].y = ty; 88 vis[v] = true; 89 DFS(v); 90 } 91 } 92 93 int main() 94 { 95 while (~scanf("%d%d", &n, &m)) 96 { 97 for (int i = 0; i <= n; i++) mp[i].clear(); 98 stps.clear(); 99 for (int i = 1; i <= m; i++) 100 { 101 int u, v, l; 102 char op[10]; 103 scanf("%d%d%d%s", &u, &v, &l,op); 104 mp[u].push_back(node(v, op[0], l)); 105 char nc; 106 switch (op[0]) 107 { 108 case 'N': 109 nc = 'S'; 110 break; 111 case 'S': 112 nc = 'N'; 113 break; 114 case 'E': 115 nc = 'W'; 116 break; 117 case 'W': 118 nc = 'E'; 119 break; 120 } 121 mp[v].push_back(node(u, nc, l)); 122 stps.push_back(stp(u, v)); 123 } 124 memset(vis, 0, sizeof(vis)); 125 for (int i = 0; i <= n; i++) 126 { 127 if (mp[i].size()) 128 { 129 points[i].x = 0, points[i].y = 0; 130 vis[i] = true; 131 DFS(i); 132 break; 133 } 134 } 135 scanf("%d", &k); 136 for (int i = 0; i < k; i++) 137 { 138 scanf("%d%d%d",&qrys[i].f1, &qrys[i].f2, &qrys[i].t); 139 qrys[i].id = i + 1; 140 } 141 sort(qrys, qrys + k, Cmp1); 142 143 for (int i = 0; i <= n; i++) pre[i] = i; 144 int nowstep = 0; 145 for (int i = 0; i < k; i++) 146 { 147 int t = qrys[i].t; 148 for (; nowstep < t; nowstep++) 149 { 150 int f1 = stps[nowstep].f1; 151 int f2 = stps[nowstep].f2; 152 Union(f1, f2); 153 } 154 int x = qrys[i].f1, y = qrys[i].f2; 155 int fx = Find(x), fy = Find(y); 156 if (fx != fy) 157 { 158 ans[qrys[i].id] = -1; 159 } 160 else ans[qrys[i].id] = abs(points[x].x - points[y].x) + abs(points[x].y - points[y].y); 161 } 162 for (int i = 1; i <= k; i++) 163 { 164 printf("%d ", ans[i]); 165 } 166 } 167 return 0; 168 }
10、hdu 1829 A Bug's Life
题意:给定一系列数对,例如a和b,表示a和b不是同一种性别,然后不断的给出这样的数对,问有没有矛盾的情况。
思路:带权并查集。
1 #include<iostream> 2 using namespace std; 3 const int maxn = 2100; 4 int pre[maxn]; 5 int r[maxn]; 6 int n, m; 7 void Init() 8 { 9 for (int i = 0; i <= n; i++) 10 { 11 pre[i] = i; 12 r[i] = 0; 13 } 14 } 15 int Find(int x) 16 { 17 if (pre[x] == x)return x; 18 else 19 { 20 int fa = pre[x]; 21 pre[x] = Find(fa); 22 r[x] = r[x] ^ r[fa]; 23 return pre[x]; 24 } 25 } 26 27 bool Join(int x, int y) 28 { 29 int fx = Find(x), fy = Find(y); 30 if (fx == fy)return true; 31 else 32 { 33 pre[fx] = fy; 34 r[fx] = r[x] ^ r[y] ^ 1; 35 return false; 36 } 37 } 38 int main() 39 { 40 int t; 41 int Case = 1; 42 scanf("%d", &t); 43 while (t--) 44 { 45 scanf("%d%d", &n, &m); 46 Init(); 47 bool flag = true; 48 while (m--) 49 { 50 int u, v; 51 scanf("%d%d", &u, &v); 52 if (!flag)continue; 53 if (Join(u, v)) 54 { 55 if (r[u] ^ r[v] != 1) 56 { 57 flag = false; 58 } 59 } 60 } 61 if (flag) printf("Scenario #%d: No suspicious bugs found! ", Case++); 62 else printf("Scenario #%d: Suspicious bugs found! ", Case++); 63 } 64 return 0; 65 }
11、poj 2912 Rochambeau
题意:有N个人玩剪刀石头布,其中有个人是裁判,其他人分为3组。 这3组中每个组分别出剪刀,石头和布。 裁判可以任意出3个中的一个。 现在有M个回合,每个回合从N个人中任意选出两个人来玩,并给出结果。 要求输出最早找出裁判的回合数,以及裁判的编号。还有可能无法确定。
思路:利用并查集建立起相对关系,枚举裁判是哪一个,对有他参与的回合不予处理,如果在他不参与的回合中出现了矛盾,那么这个人一定就不是裁判。
1 #include<iostream> 2 using namespace std; 3 const int maxn = 510; 4 const int maxm = 2010; 5 int n, m; 6 int pre[maxn]; 7 int r[maxn];//0:平局;1:pre[x]赢了x;2:x赢了pre[x] 8 void Init() 9 { 10 for (int i = 0; i <= n; i++) 11 { 12 pre[i] = i; 13 r[i] = 0; 14 } 15 } 16 struct stp 17 { 18 int u; 19 int v; 20 int d; 21 }stps[maxm]; 22 23 int ok[maxn]; 24 25 int Find(int x) 26 { 27 if (pre[x] == x)return x; 28 else 29 { 30 int fa = pre[x]; 31 pre[x] = Find(fa); 32 r[x] = (r[x] + r[fa]) % 3; 33 return pre[x]; 34 } 35 } 36 bool Union(int x, int y,int d) 37 { 38 int fx = Find(x), fy = Find(y); 39 if (fx == fy)return true; 40 else 41 { 42 pre[fx] = fy; 43 r[fx] = (3 - r[x] + d + r[y]) % 3; 44 return false; 45 } 46 } 47 int main() 48 { 49 while (~scanf("%d%d", &n, &m)) 50 { 51 for (int i = 0; i < m; i++) 52 { 53 int u, v,d; 54 char c; 55 scanf("%d%c%d", &u, &c, &v); 56 if (c == '=')d = 0; 57 else if (c == '<') d = 1; 58 else d = 2; 59 stps[i].u = u, stps[i].v=v, stps[i].d=d; 60 } 61 memset(ok, 0, sizeof(ok)); 62 for (int i = 0; i < n; i++) 63 { 64 Init(); 65 for (int j = 0; j < m; j++) 66 { 67 if (stps[j].u == i || stps[j].v == i)continue; 68 if (Union(stps[j].u, stps[j].v, stps[j].d)) 69 { 70 if ((r[stps[j].u]+3-r[stps[j].v]) % 3 != stps[j].d) 71 { 72 ok[i] = j + 1; 73 break; 74 } 75 } 76 } 77 } 78 int cnt = 0,rline=0,judgeperson=0; 79 for (int i = 0; i < n; i++) 80 { 81 if (ok[i]== 0) 82 { 83 cnt++; 84 judgeperson = i; 85 } 86 if (ok[i] > rline) rline = ok[i]; 87 } 88 if (cnt == 0)printf("Impossible "); 89 else if (cnt == 1)printf("Player %d can be determined to be the judge after %d lines ",judgeperson,rline); 90 else printf("Can not determine "); 91 } 92 return 0; 93 }
12、hdu 1272 小希的迷宫
题意:如果有一个通道连通了房间A和B,那么既可以通过它从房间A走到房间B,也可以通过它从房间B走到房间A,为了提高难度,小希希望任意两个房间有且仅有一条路径可以相通(除非走了回头路)。小希现在把她的设计图给你,让你帮忙判断她的设计图是否符合她的设计思路。
思路:并查集。
1 #include<algorithm> 2 using namespace std; 3 const int maxn = 100010; 4 int pre[maxn]; 5 bool vis[maxn]; 6 int Find(int x) 7 { 8 if (pre[x] == x)return x; 9 else 10 { 11 int fa = pre[x]; 12 pre[x] = Find(fa); 13 return pre[x]; 14 } 15 } 16 17 bool Union(int x, int y) 18 { 19 int fx = Find(x), fy = Find(y); 20 if (fx == fy)return true; 21 else 22 { 23 pre[fx] = fy; 24 return false; 25 } 26 } 27 int main() 28 { 29 int u, v; 30 while (~scanf("%d%d", &u, &v)) 31 { 32 if (u == -1 && v == -1)break; 33 if (u == 0 && v == 0) 34 { 35 printf("Yes "); 36 continue; 37 } 38 for (int i = 0; i < maxn; i++)pre[i] = i; 39 bool flag = true; 40 do 41 { 42 if (!flag)continue; 43 if (Union(u, v)) 44 { 45 flag = false; 46 } 47 } while (scanf("%d%d", &u, &v) && u != 0 && v != 0); 48 if (!flag) printf("No "); 49 else 50 { 51 memset(vis, 0, sizeof(vis)); 52 int cnt = 0; 53 for (int i = 0; i < maxn; i++) 54 { 55 if (pre[i] == i)continue; 56 int f = Find(i); 57 if (!vis[f]) 58 { 59 cnt++; 60 vis[f] = true; 61 } 62 } 63 if (cnt == 1) printf("Yes ");//只能有一个连通块 64 else printf("No "); 65 } 66 } 67 return 0; 68 }
13、poj 1308 Is It A Tree
题意:输入数据表示存在父子节点关系的点的编号,判断是否为树。
思路:并查集。
1 #include<algorithm> 2 using namespace std; 3 const int maxn = 100010; 4 int pre[maxn]; 5 bool vis[maxn]; 6 int Find(int x) 7 { 8 if (pre[x] == x)return x; 9 else 10 { 11 int fa = pre[x]; 12 pre[x] = Find(fa); 13 return pre[x]; 14 } 15 } 16 17 bool Union(int x, int y) 18 { 19 int fx = Find(x), fy = Find(y); 20 if (fx == fy)return true; 21 else 22 { 23 pre[fx] = fy; 24 return false; 25 } 26 } 27 int main() 28 { 29 int u, v; 30 int Case = 1; 31 while (~scanf("%d%d", &u, &v)) 32 { 33 if (u<0 && v <0)break; 34 if (u == 0 && v == 0) 35 { 36 printf("Case %d is a tree. ",Case++); 37 continue; 38 } 39 for (int i = 0; i < maxn; i++)pre[i] = i; 40 bool flag = true; 41 do 42 { 43 if (!flag)continue; 44 if (Union(u, v)) 45 { 46 flag = false; 47 } 48 } while (scanf("%d%d", &u, &v) && u != 0 && v != 0); 49 if (!flag) printf("Case %d is not a tree. ",Case++); 50 else 51 { 52 memset(vis, 0, sizeof(vis)); 53 int cnt = 0; 54 for (int i = 0; i < maxn; i++) 55 { 56 if (pre[i] == i)continue; 57 int f = Find(i); 58 if (!vis[f]) 59 { 60 cnt++; 61 vis[f] = true; 62 } 63 } 64 if (cnt == 1) printf("Case %d is a tree. ", Case++);//只能有一个连通块 65 else printf("Case %d is not a tree. ",Case++); 66 } 67 } 68 return 0; 69 }
14、LA 3644 X-Plosives
题意:给出若干对,表示a和b相连,要保证每个连通块为一颗树,要删去哪些对。
思路:并查集。
1 #include<iostream> 2 using namespace std; 3 const int maxn = 100100; 4 int pre[maxn]; 5 int Find(int x) 6 { 7 if (x == pre[x]) return x; 8 else 9 { 10 int fa = pre[x]; 11 pre[x] = Find(fa); 12 return pre[x]; 13 } 14 } 15 bool Union(int x, int y) 16 { 17 int fx = Find(x), fy = Find(y); 18 if (fx == fy) return true; 19 else 20 { 21 pre[fx] = fy; 22 return false; 23 } 24 } 25 int main() 26 { 27 int a,b; 28 while (~scanf("%d", &a)) 29 { 30 scanf("%d", &b); 31 for (int i = 0; i < maxn; i++) pre[i] = i; 32 Union(a, b); 33 int cnt = 0; 34 while (scanf("%d", &a) && a != -1) 35 { 36 scanf("%d", &b); 37 if (Union(a, b)) cnt++; 38 } 39 printf("%d ", cnt); 40 } 41 return 0; 42 }
15、LA 3027 Corporative Network
题意:每次可以询问一家公司到其所在的群的中心公司的线路长度,或者将某个群的中心公司A接到另一个群的某家公司B,并且两个群所有的公司的中心改为原来B所在群的中心公司。
思路:带权并查集。
1 #include<iostream> 2 #include<cmath> 3 #include<cstdio> 4 using namespace std; 5 const int maxn = 20010; 6 int pre[maxn]; 7 int val[maxn]; 8 int n; 9 void Init() 10 { 11 for (int i = 0; i <= n; i++) pre[i] = i, val[i] = 0; 12 } 13 int Find(int x) 14 { 15 if (x == pre[x]) return x; 16 else 17 { 18 int fa = pre[x]; 19 pre[x] = Find(fa); 20 val[x] = val[x] + val[fa]; 21 return pre[x]; 22 } 23 } 24 bool Union(int x, int y,int l) 25 {//I x y 26 int fx = Find(x), fy = Find(y); 27 if (fx == fy) return true; 28 else 29 { 30 pre[fx] = fy; 31 val[fx] = l + val[y]; 32 return false; 33 } 34 } 35 int main() 36 { 37 int t; 38 scanf("%d", &t); 39 char s[5]; 40 while (t--) 41 { 42 scanf("%d", &n); 43 Init(); 44 while (~scanf("%s", s)) 45 { 46 if (s[0] == 'O')break; 47 if (s[0] == 'E') 48 { 49 int x; 50 scanf("%d", &x); 51 Find(x); 52 printf("%d ", val[x]); 53 } 54 else 55 { 56 int x, y; 57 scanf("%d%d", &x, &y); 58 Union(x, y, abs(x - y) % 1000); 59 } 60 } 61 } 62 return 0; 63 }
16、LA 4487 Exclusive-OR
题意:每次告诉你A的值是多少或者A和B异或的结果是多少,或者询问k个数连续异或的值。
思路:带权并查集。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 const int maxn = 21000; 6 int pre[maxn], re[maxn]; 7 int vis[maxn], num[20]; 8 int supperroot = maxn - 10; 9 void init(int n) 10 { 11 for (int i = 0; i <= n + 1; i++) 12 { 13 pre[i] = i; 14 re[i] = 0; 15 vis[i] = 0; 16 } 17 pre[supperroot] = supperroot; 18 re[supperroot] = 0; 19 } 20 int Find(int x) 21 { 22 if (x != pre[x]) 23 { 24 int fa = pre[x]; 25 pre[x] = Find(pre[x]); 26 re[x] ^= re[fa]; 27 } 28 return pre[x]; 29 } 30 bool Union(int x, int y, int val) 31 { 32 int fx = Find(x); 33 int fy = Find(y); 34 if (fx == fy) 35 { 36 if ((re[x] ^ re[y]) != val) return 0; 37 else return 1; 38 } 39 if (fx == supperroot) swap(fx, fy); 40 pre[fx] = fy; 41 re[fx] = re[x] ^ re[y] ^ val; 42 return 1; 43 } 44 int main() 45 { 46 int n, Q, p, q, val, kase = 0; 47 char ch[2]; 48 char s[40]; 49 while (scanf("%d%d", &n, &Q)) 50 { 51 if (n==0&&Q==0) break; 52 init(n); 53 printf("Case %d: ", ++kase); 54 bool flag = 0; 55 int facts = 0; 56 for (int i = 1; i <= Q; i++) 57 { 58 scanf("%s", ch); 59 if (ch[0] == 'I') 60 { 61 facts++; 62 gets(s); 63 if (sscanf(s, "%d%d%d", &p, &q, &val) == 2) 64 { 65 val = q; 66 q = supperroot; 67 } 68 if (flag) continue; 69 if (!Union(p, q, val)) 70 { 71 printf("The first %d facts are conflicting. ", facts); 72 flag = 1; 73 } 74 } 75 else 76 { 77 int k, ans = 0; 78 scanf("%d", &k); 79 for (int i = 1; i <= k; i++) 80 { 81 scanf("%d", &num[i]); 82 if (flag) continue; 83 int fa = Find(num[i]); 84 ans ^= re[num[i]]; 85 num[i] = fa; 86 vis[fa] ^= 1; 87 } 88 if (flag) continue; 89 bool unknow = false; 90 for (int i = 1; i <= k; i++) 91 { 92 if (vis[num[i]]) 93 { 94 if (num[i] != supperroot) 95 { 96 unknow = true; 97 } 98 vis[num[i]] = 0; 99 } 100 } 101 if (!unknow) printf("%d ", ans); 102 else printf("I don't know. "); 103 } 104 } 105 printf(" "); 106 } 107 return 0; 108 }
17、uva 11987 Almost Union-Find
题意:第一种操作为把P和Q所在集合和并,第二种为把结点P放到Q所在集合里,第三种是查询P所在集合的元素数目和总和。
思路:带权并查集。初始时把每个结点的祖先指向i+n,这样当第二种操作时,就不会影响P原来集合的根。
1 #include<iostream> 2 using namespace std; 3 const int maxn = 100100; 4 int pre[maxn]; 5 int sum[maxn]; 6 int cnt[maxn]; 7 int n, m; 8 int Find(int x) 9 { 10 if (x == pre[x]) return x; 11 else 12 { 13 int fa = pre[x]; 14 pre[x] = Find(fa); 15 return pre[x]; 16 } 17 } 18 void Union(int x, int y) 19 { 20 int fx = Find(x), fy = Find(y); 21 if (fx != fy) 22 { 23 pre[fx] = fy; 24 sum[fy] += sum[fx]; 25 cnt[fy] += cnt[fx]; 26 sum[fx] = 0; 27 cnt[fx] = 0; 28 } 29 } 30 void Change(int x, int to) 31 { 32 int f = Find(to); 33 int init = Find(x); 34 pre[x] = f; 35 sum[f] += x, cnt[f]++; 36 sum[init] -= x, cnt[init]--; 37 } 38 int main() 39 { 40 while (~scanf("%d%d", &n, &m)) 41 { 42 for (int i = 0; i <= n; i++) pre[i] = i+n, cnt[i+n] = 1, sum[i+n] = i,pre[i+n]=i+n; 43 int s; 44 while (m--) 45 { 46 scanf("%d", &s); 47 if (s == 1) 48 { 49 int p, q; 50 scanf("%d%d", &p, &q); 51 Union(p, q); 52 } 53 else if (s == 2) 54 { 55 int p, q; 56 scanf("%d%d", &p, &q); 57 Change(p, q); 58 } 59 else 60 { 61 int p; 62 scanf("%d", &p); 63 printf("%d %d ", cnt[Find(p)], sum[Find(p)]); 64 } 65 } 66 } 67 return 0; 68 }
18、LA 5898/uva 1493/hdu 4056 Draw a Mess
题意:有若干个人按照顺序在n*m的黑板上画4种图形。求最后各种颜色所占的像素数目。
思路:用并查集维护每行当前位置的下一个可以画的位置,然后倒序更新。
1 #include<cmath> 2 #include<cstdio> 3 #include<algorithm> 4 #include<iostream> 5 #include<cstring> 6 using namespace std; 7 8 const int maxn = 205; 9 const int maxm=100500; 10 char str[maxm][25]; 11 int xx[maxm], yy[maxm], p[maxm], t[maxm], c[maxm]; 12 int Next[maxn][maxm]; 13 int Find(int i, int t) 14 { 15 if (Next[i][t] != t) 16 Next[i][t] = Find(i, Next[i][t]); 17 return Next[i][t]; 18 } 19 int dele(int i, int l, int r) 20 { 21 int ret = 0; 22 l = Find(i, l); 23 while (l <= r) 24 { 25 ret++; 26 Next[i][l] = l + 1; 27 l = Find(i, l); 28 } 29 return ret; 30 } 31 int main() 32 { 33 int n, m, q; 34 int i, j, x, y, r, w, l, h, color; 35 int tmp1, tmp2, tmp3; 36 int ans[10]; 37 while (~scanf("%d%d%d", &n, &m, &q)) 38 { 39 for (i = 0; i<n; i++) 40 for (j = 0; j <= m; j++) 41 Next[i][j] = j; 42 memset(ans, 0, sizeof(ans)); 43 for (i = 0; i<q; i++) 44 { 45 scanf("%s%d%d%d%d", str[i], &xx[i], &yy[i], &tmp1, &tmp2); 46 if (str[i][0] != 'R') 47 p[i] = tmp1, c[i] = tmp2; 48 else 49 { 50 scanf("%d", &tmp3); 51 p[i] = tmp1; t[i] = tmp2; c[i] = tmp3; 52 } 53 } 54 for (i = q - 1; i >= 0; i--) 55 { 56 x = xx[i], y = yy[i]; 57 if (str[i][0] == 'D') 58 {//菱形 59 r = p[i], color = c[i]; 60 for (j = max(0, x - r); j <= min(n - 1, x + r); j++)//枚举行 61 ans[color] += dele(j, max(0, y - r + abs(x - j)),min(m - 1, y + r - abs(x - j))); 62 } 63 else if (str[i][0] == 'T') 64 {//等腰三角形 65 w = p[i], color = c[i]; 66 h = (w + 1) / 2; 67 for (j = max(0, x); j<min(n, x + h); j++) 68 ans[color] += dele(j,max(0, y - h + j - x + 1),min(m - 1, y + h - j + x - 1)); 69 } 70 else if (str[i][0] == 'R') 71 {//矩形 72 l = p[i], w = t[i], color = c[i]; 73 for (j = max(0, x); j <= min(n - 1, x + l - 1); j++) 74 ans[color] += dele(j, y,min(m - 1, y + w - 1)); 75 } 76 else if (str[i][0] == 'C') 77 {//圆形 78 r = p[i]; color = c[i]; 79 for (j = max(0, x - r); j <= min(n - 1, x + r); j++) 80 { 81 int tmp = floor(sqrt(1ll * r*r - (j - x)*(j - x))); 82 ans[color] += dele(j, max(0, y - tmp), min(m - 1, y + tmp)); 83 } 84 } 85 } 86 for (i = 1; i <= 9; i++) 87 { 88 if (i > 1) printf(" "); 89 printf("%d", ans[i]); 90 } 91 printf(" "); 92 } 93 return 0; 94 }
19、OpenJ_POJ - C15C Rabbit's Festival
题意:每个结点都住着一只兔子,一共有n个结点,有m条无向边,每条边在第ci天时无法通行。问从1~k天中,每天可以两两相遇的兔子的对数?
思路:某一天可以相遇,则在同一个并查集当中,易得对数。怎么维护这个并查集呢?CDQ分治。参考:http://www.bubuko.com/infodetail-1220058.html
1 //CDQ分治+并查集 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 const int maxn = 100010; 7 const int maxe = 200010; 8 struct edge 9 { 10 int from, to,next; 11 edge(int ff=0,int tt=0,int nn=0):from(ff),to(tt),next(nn){} 12 }Edge[maxe]; 13 int Head[maxn], totedge; 14 int pre[maxn], cnt[maxn];//其父结点、以其为根的树的结点个数 15 int n, m, k; 16 void addedge(int from, int to, int c) 17 { 18 Edge[totedge] = edge(from, to, Head[c]); 19 Head[c] = totedge++; 20 } 21 void init() 22 { 23 for (int i = 1; i <= n; i++) pre[i] = i, cnt[i] = 1; 24 memset(Head, -1, sizeof(Head)); 25 totedge = 0; 26 } 27 int find(int x) 28 {//找到根结点,不用路径压缩 29 while (pre[x] != x) 30 { 31 x = pre[x]; 32 } 33 return x; 34 } 35 long long Cal(int x) 36 {//以x为根的所有结点可以形成的配对对数 37 return 1ll * x*(x - 1) / 2; 38 } 39 struct CDQ 40 { 41 long long result; 42 pair<int, int> stk[maxe];//保存最近添加的边,方便从并查集删去 43 int stkTop; 44 void Push(edge&cur) 45 {//将边cur加入并查集 46 int a = find(cur.from); 47 int b = find(cur.to); 48 if (a == b) return; 49 result -= Cal(cnt[a]); 50 result -= Cal(cnt[b]); 51 if (cnt[a] > cnt[b]) swap(a, b); 52 cnt[b] += cnt[a]; 53 pre[a] = b; 54 result += Cal(cnt[b]); 55 56 stk[++stkTop] = make_pair(a, b); 57 } 58 void Push(int l, int r) 59 {//从l~r天所有不可通的道路加入并查集(即当前遍历的天数不在l~r中) 60 for (int i = l; i <= r; i++) 61 { 62 for (int j = Head[i]; j != -1; j = Edge[j].next) 63 { 64 Push(Edge[j]); 65 } 66 } 67 } 68 void Pop(int l, int r) 69 { 70 for (int i = r; i >= l; i--) 71 { 72 int u = stk[i].first; 73 int v = stk[i].second; 74 result -= Cal(cnt[v]); 75 cnt[v] -= cnt[u]; 76 result += Cal(cnt[u]); 77 result += Cal(cnt[v]); 78 pre[u] = u; 79 } 80 } 81 82 void cdq(int l, int r) 83 {//将从l~r天可以连通的道路加入并查集,不可连通的则从并查集删去 84 if (l == r) 85 {//如果刚好第l天没有连通的道路不在并查集中 86 printf("%lld ", result); 87 return; 88 } 89 int mid = (l + r) >> 1; 90 int tmp = stkTop; 91 Push(mid + 1, r);//考虑l~mid天,将第mid+1~r天不可连通的道路加入并查集 92 cdq(l, mid); 93 Pop(tmp + 1, stkTop);//把加入的边删去 94 stkTop = tmp; 95 Push(l,mid);//考虑mid+1~r天,将第l~mid天不可连通的道路加入并查集 96 cdq(mid + 1, r); 97 Pop(tmp + 1, stkTop); 98 stkTop = tmp; 99 } 100 void cdqinit() 101 { 102 stkTop = -1; 103 result = 0; 104 } 105 void Run() 106 { 107 cdq(1, k); 108 } 109 }CDQ; 110 int main() 111 { 112 while (~scanf("%d%d%d", &n, &m, &k)) 113 { 114 init(); 115 CDQ.cdqinit(); 116 for (int i = 1; i <= m; i++) 117 { 118 int u, v, c; 119 scanf("%d%d%d", &u, &v, &c); 120 if (c <= k) 121 {//如果1~k中有某一天不通,则加到对应边中 122 addedge(u, v, c); 123 continue; 124 } 125 //将从1~k天都能连通的路直接连起来 126 edge tmp = edge(u, v, 0); 127 CDQ.Push(tmp); 128 } 129 CDQ.Run(); 130 } 131 return 0; 132 }