一、题目描述
Description
有N个人编号从1 ~ N,M对朋友关系和K对敌对关系。
现在给每个人找候选朋友,候选朋友是他的间接朋友(朋友的朋友),但不能与他是敌对关系。
问每个人有多少个候选朋友。
Input
第一行为三个整数N,M,K。
后面M行,每行两个整数Ai,Bi,表示朋友关系。
接下来K行,每行两个整数Ci,Di,表示敌对关系。
2 ≤ N ≤ 105
0 ≤ M , K ≤ 105
1 ≤ Ai , Bi ≤ N
1 ≤ Ci , Di ≤ N
Ai ≠ Bi , Ci ≠ Di
(Ai , Bi) ≠ (Aj , Bj) (i ≠ j)
(Ai , Bi) ≠ (Bj , Aj)
(Ci , Di) ≠ (Cj , Dj) (i ≠ j)
(Ci , Di) ≠ (Dj , Cj)
(Ai , Bi) ≠ (Cj , Dj)
(Ai , Bi) ≠ (Dj , Cj)
Output
从编号1 ~ N,依次输出他有多少个候选朋友,中间留空格。
Sample Input Copy
4 4 1
2 1
1 3
3 2
3 4
4 1
Sample Output Copy
0 1 0 1
二、解题思路
我们可以直接使用并查集获得每个朋友的朋友总数(朋友总数等于父节点的子节点个数),然后将朋友总数减去候选朋友和敌对朋友的和即可(注意:如果敌对的朋友不属于同一个朋友圈中就没有必要记录这个数了,他们都不在一个朋友圈中,自然也不可能是朋友)。我们使用两个数组,一个数组记录当前该人的总朋友的个数,一个数组记录当前该人的候选朋友的数量加敌对朋友的数量,最后遍历相减,可AC。
三、代码实现
#include <iostream>
using namespace std;
int pre[200010];
int n, m, k, a, b;
int ct1[200010], ct2[200010];
int find(int x) {
return pre[x] == x ? x : pre[x] = find(pre[x]);
}
void join(int x, int y) {
int fx = find(x), fy = find(y);
if (fx != fy) {
pre[fy] = fx;
ct1[fx] += ct1[fy];//记录朋友圈里的总人数
}
}
int main()
{
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= n; i++) {
pre[i] = i; ct1[i] = 1;
}
for (int i = 0; i < m; i++) {
scanf("%d%d", &a, &b);
join(a, b);
ct2[a]++; ct2[b]++;//记录候选朋友个数
}
for (int i = 0; i < k; i++) {
scanf("%d%d", &a, &b);
int kk = find(a), s = find(b);
if (kk == s) {//两人是否在同一个圈子中,是就必须记录该敌对数
ct2[a]++;
ct2[b]++;
}
}
for (int i = 1; i <= n; i++) {//遍历相减
if (i == n) printf("%d\n", ct1[find(i)] - ct2[i] - 1);//减一代表减去自己
else printf("%d ", ct1[find(i)] - ct2[i] - 1);
}
return 0;
}