Codeforces 745C:Hongcow Builds A Nation(并查集)

http://codeforces.com/problemset/problem/744/A

题意:在一个图里面有n个点m条边,还有k个点是受限制的,即不能从一个受限制的点走到另外一个受限制的点(有路径相连),问在这样的图里面遵守这样的规则可以最多添加几条边。

思路:这种题之前在做强连通的时候很常见,于是就写了tarjan。。醒来后发现不用这么复杂,直接用并查集就可以做了。

1、对于每一个连通块,最多可以加上n*(n-1)/2条边。

2、对于受限制的连通块,取出一个点数最多的,和不受限制的块相连。

3、对于不受限制的连通块,两两之间可以相连。

4、由于有重复,所以减去初始的m条边。

并查集:

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <iostream>
 4 #include <cstring>
 5 #include <string>
 6 #include <cmath>
 7 #include <queue>
 8 #include <vector>
 9 #include <map>
10 #include <set>
11 using namespace std;
12 #define INF 0x3f3f3f3f
13 #define N 100010
14 typedef long long LL;
15 int sum[1010], c[1010], fa[1010], tag[1010];
16 vector<int> vec;
17
18 int Find(int x) {
19     if(x == fa[x]) return x;
20     return fa[x] = Find(fa[x]);
21 }
22
23 void Merge(int x, int y) {
24     x = Find(x), y = Find(y);
25     if(x == y) return ;
26     fa[x] = y;
27     sum[y] += sum[x];
28 }
29
30 int main() {
31     int n, m, k;
32     cin >> n >> m >> k;
33     for(int i = 1; i <= k; i++) cin >> c[i];
34     for(int i = 1; i <= n; i++) fa[i] = i, sum[i] = 1;
35     for(int i = 1; i <= m; i++) {
36         int u, v;
37         cin >> u >> v;
38         Merge(u, v);
39     }
40     int ans = -m, ma = 0;
41     for(int i = 1; i <= k; i++) tag[Find(i)] = 1; // 标记受限制的块
42     for(int i = 1; i <= n; i++) {
43         if(fa[i] == i) {
44             if(tag[i]) ma = max(sum[i], ma);
45             else vec.push_back(sum[i]);
46             ans += sum[i] * (sum[i] - 1) / 2; // 每个连通块里面最大边数
47         }
48     }
49     int sz = vec.size();
50     for(int i = 0; i < sz; i++) {
51         for(int j = i + 1; j < sz; j++) {
52             ans += vec[i] * vec[j]; // 不受限制的连通块之间连边
53         }
54         ans += vec[i] * ma; // 受限制的取最大的和不受限制的连边
55     }
56     cout << ans << endl;
57     return 0;
58 }

tarjan:

  1 #include <cstdio>
  2 #include <algorithm>
  3 #include <iostream>
  4 #include <cstring>
  5 #include <string>
  6 #include <cmath>
  7 #include <queue>
  8 #include <vector>
  9 #include <map>
 10 #include <set>
 11 #include <stack>
 12 using namespace std;
 13 #define INF 0x3f3f3f3f
 14 #define N 100010
 15 typedef long long LL;
 16 struct node {
 17     int u, v, nxt;
 18 }edge[2*N];
 19 struct P {
 20     int id, num;
 21 }p[1010];
 22 int head[1010], tot, cnt, num;
 23 int belong[1010], dfn[1010], low[1010], c[1010], vis[1010];
 24 stack<int> sta;
 25 vector<int> v1, v2;
 26
 27 void add(int u, int v) {
 28     edge[tot].v = v; edge[tot].nxt = head[u]; head[u] = tot++;
 29 }
 30
 31 void tarjan(int u) {
 32     dfn[u] = low[u] = ++cnt;
 33     vis[u] = 1; sta.push(u);
 34     for(int i = head[u]; ~i; i = edge[i].nxt) {
 35         int v = edge[i].v;
 36         if(!dfn[v]) {
 37             tarjan(v);
 38             if(low[v] < low[u]) low[u] = low[v];
 39         } else if(vis[v]) {
 40             if(dfn[v] < low[u]) low[u] = dfn[v];
 41         }
 42     }
 43     if(dfn[u] == low[u]) {
 44         ++num;
 45         while(true) {
 46             int v = sta.top(); sta.pop();
 47             belong[v] = num; vis[v] = 0;
 48             if(v == u) break;
 49         }
 50     }
 51 }
 52
 53 int main() {
 54     int n, m, k;
 55     scanf("%d%d%d", &n, &m, &k);
 56     for(int i = 0; i < k; i++) scanf("%d", c + i);
 57     memset(head, -1, sizeof(head));
 58     memset(vis, 0, sizeof(vis));
 59     tot = num = cnt = 0;
 60     for(int i = 0; i < m; i++) {
 61         int u, v;
 62         scanf("%d%d", &u, &v);
 63         add(u, v); add(v, u);
 64     }
 65     for(int i = 1; i <= n; i++)
 66         if(!dfn[i]) tarjan(i);
 67     v1.clear(); v2.clear();
 68     for(int i = 1; i <= num; i++) {
 69         for(int j = 1; j <= n; j++) {
 70             if(belong[j] == i) {
 71                 p[i].num++;
 72             }
 73         }
 74     }
 75     for(int i = 0; i < k; i++)
 76         p[belong[c[i]]].id = 1;
 77     for(int i = 1; i <= num; i++) {
 78         if(p[i].id == 1) v1.push_back(p[i].num);
 79         else v2.push_back(p[i].num);
 80     }
 81     sort(v1.begin(), v1.end());
 82     sort(v2.begin(), v2.end());
 83     int sz = v1.size();
 84     int szz = v2.size();
 85     LL ans = 0;
 86     for(int i = 0; i < szz; i++)
 87         ans += v1[sz-1] * v2[i];
 88     for(int i = 0; i < szz; i++) {
 89         for(int j = i + 1; j < szz; j++) {
 90             ans += v2[i] * v2[j];
 91         }
 92         ans += v2[i] * (v2[i]-1) / 2;
 93     }
 94
 95     for(int i = 0; i < sz; i++)
 96         ans += (v1[i]-1) * v1[i] / 2;
 97     ans -= m;
 98     printf("%I64d\n", ans);
 99     return 0;
100 }
101
102 /*
103 8 4 2
104 4 5
105 1 2
106 6 7
107 7 8
108 2 4
109 */
时间: 2024-10-01 02:29:32

Codeforces 745C:Hongcow Builds A Nation(并查集)的相关文章

CodeForces 745C Hongcow Builds A Nation 并查集

题意: 给了你n个城市 m条边 k个政府 每个政府管辖的区域内不能和其他政府的区域有相连 即政府之间不存在路径 问你在维护这种关系的同时 最多再加多少条边 思路: 先找出来每个联通块 再找出来没有归属的孤立的点 把他们都放到最大的联通块里 然后每个联通块之间的点两两连边是n*(n-1)/2条边 最后算出来的ans-m就好了 (看别人的博客学了一个max_element 1 #include<bits/stdc++.h> 2 #define cl(a,b) memset(a,b,sizeof(a

CodeForces - 744A Hongcow Builds A Nation

http://codeforces.com/problemset/problem/744/A 这是一道考察连通块的题(做之前, 连通块是什么都不清楚) Note:点的集合 任意两点都有可达的路径 可以用并查集做 在一个政府管辖下的点 作为一个集合 根节点就是这个政府 再者 贪心 : 要求做多可以添加的边数 做多一个集合 n个点 使它构成完全图 得到的边数为n*(n-1) / 2 那么最终表达式可以为n[1]*(n[1]-1) / 2 + n[2]*(n[2]-1) / 2 +....+ n[k]

CodeForces 776D The Door Problem【并查集】

CodeForces 776D The Door Problem[并查集]并查集 设 f 1--m 表示 开的情况 m+1--2*m 表示关的情况 对于每盏灯 如果他 是关的 则 x--y x+m--y+m 表示要同关 或者同开 如果他 是开的 则 x+m--y x--y+m 表示一个关 一个开如果一盏灯 的 x 连向 了 x+m 则表示是矛盾了 那么久是错误的 题意:给你n个门,和m组开关,每扇门都有两个开关控制,每个开关控制x扇门,如果选择了某组开关,则使这组开关里的每个开关控制的所有的门按

Codeforces Round #250 (Div. 1) B 并查集

坑!神坑!深坑!,WA了几十把,最终答案  (ans * 2)/(n * 1.0 * (n - 1)) 要是写成(ans * 2)/(n *(n - 1)*1.0)就是WA,不明白为啥,愤怒的我 全改成double就可以了,若前面变量用了int的 答案必须是前一种写法, 题目不是特别难,没啥思路画一画就有思路了,10^5的n去扫肯定是要超时的,那就想想一次性的10^5,发想通过m是可以的,建边,边权就是两端点中小的那个,然后对最终答案的种数进行分析,发现其实就是 每次你要连接的两块连通块的个数相

Codeforces Round #541 (Div. 2) D 并查集 + 拓扑排序

https://codeforces.com/contest/1131/problem/D 题意 给你一个n*m二维偏序表,代表x[i]和y[j]的大小关系,根据表构造大小分别为n,m的x[],y[],使得两个数组中最大的数尽量小 题解 按照偏序表,构造出从小到大的拓扑图 如何解决相等的数的偏序关系? 用并查集缩点后再进行拓扑排序 如何解决最大的数最小? 只需要使得同一层的数相同就行,可以一批处理栈中的元素,对于一批栈中的元素产生的新点,先放进一个容器里,然后等到这批栈清空了,再把这个容器中的点

codeforces 691D Swaps in Permutation(并查集)

题意: 给你一个长度为n的数列,然后给你m组数, 表示这两个数可以交换 然后让你给出字典序最大的数列 思路: 用并查集,可交换的数都是成组的,把同一并查集中的数加在根节点的vector后, 在一个并查集中的数,从大到输出就好了 /* *********************************************** Author :devil ************************************************ */ #include <cstdio>

CodeForces 698B Fix a Tree (并查集应用)

当时也是想到了并查集,但是有几个地方没有想清楚,所以就不知道怎么写了,比如说如何确定最优的问题.赛后看了一下别人的思路,才知道自己确实经验不足,思维也没跟上. 其实没有那么复杂,这个题目我们的操作只有三个 1.确定根节点.2.解环. 3连接子树. 如果题目中给出了一个或者多个根节点,我们任选一个即可,证明:假设有k个可行根节点,那么选择一个不动,改动k-1次,每种选择都是这样.但如果题目中没有可选根节点,就不可以随便去选了,首先明确这种情况一定存在了1个或者多个环,我们一定要从环中选取根节点,因

[Codeforces 1027 F] Session in BSU [并查集维护二分图匹配问题]

题面 传送门 思路 真是一道神奇的题目呢 题目本身可以转化为二分图匹配问题,要求右半部分选择的点的最大编号最小的一组完美匹配 注意到这里左边半部分有一个性质:每个点恰好连出两条边到右半部分 那么我们可以利用这个性质 考虑一个左边的点和它右边联通的两个点,发现这两个点只能选择一个和这个左边的点匹配 那么我们考虑把这个点点匹配的模型转化成点边匹配 我们在同一个左边点连的两个右边点之间连边,那么问题就变成了一个点和一条相邻的边匹配,求完美匹配的问题了 而这个问题,我们显然可以用并查集来很好的解决 考虑

Codeforces.1051G.Distinctification(线段树合并 并查集)

题目链接 \(Description\) 给定\(n\)个数对\(A_i,B_i\).你可以进行任意次以下两种操作: 选择一个位置\(i\),令\(A_i=A_i+1\),花费\(B_i\).必须存在一个位置\(j\),满足\(A_i=A_j,\ i\neq j\),才可以进行. 选择一个位置\(i\),令\(A_i=A_i-1\),花费\(-B_i\).必须存在一个位置\(j\),满足\(A_i=A_j+1\),才可以进行. 你需要对于所有\(i\in[1,n]\),求使得\(A_1,A_2,