P9008 [入门赛 #9] 大碗宽面
"我"->朋友->朋友的敌人
首先,我们可以知道朋友和敌人的关系可以用图来存储。
设\(g\)为朋友关系所存储的图,\(s\)为敌人关系所存储的东西。
如果所有人都互相握手,那么数量就是\(\frac {n(n-1)} {2}\)。
然后,我们发现\(1 \le n \le 10^6\),所以明显会超int,所以要开 long long。
接下来,因为有\(q\)对敌人关系,所以肯定要减去的。
剩下来的就是不确定关系。
可以开一个 map 来存储关系,即存储这两个人是否有确定的朋友或敌人关系。
枚举
第一重,枚举每个人,第二重,枚举当前这个人的所有朋友。
根据我们的关系式我->朋友->朋友的敌人
,可以得出,如果存在这样的关系,我
和朋友的敌人
是绝对不会握手的。
所以第三重,枚举朋友的敌人。
如果发现我
和朋友的敌人
之间还没有关系,那么就建立关系,即两人之间是敌对关系。答案自然也要减\(1\)。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<utility>
using namespace std;
const int maxn = 1000006;
vector<int> g[maxn]; // 朋友关系
vector<int> s[maxn]; // 敌对关系
int n, p, q;
long long ans; // 答案
// relation rel[{u, v}] 为真时代表两人之间有关系
map<pair<int, int>, bool> rel;
int main() {
scanf("%d%d%d", &n, &p, &q);
// 先计算能确定的握手关系
ans = (long long)n * (n - 1) / 2 - q;
for (int i = 1; i <= p; i++) {
int u, v;
scanf("%d%d", &u, &v);
g[u].push_back(v);
g[v].push_back(u);
// 记录关系
rel[{u, v}]++;
rel[{v, u}]++;
}
for (int i = 1; i <= q; i++) {
int u, v;
scanf("%d%d", &u, &v);
s[u].push_back(v);
s[v].push_back(u);
// 记录关系
rel[{u, v}]++;
rel[{v, u}]++;
}
for (int i = 1; i <= n; i++) { // 我
for (int j = 0; j < g[i].size(); j++) { // 朋友
int fri = g[i][j]; // fri 存储朋友
for (int k = 0; k < s[fri].size(); k++) { // 朋友的敌人
int a = i, b = s[fri][k];
if (!rel[{a, b}]) { // 还没有关系,就更新关系
rel[{a, b}]++;
rel[{b, a}]++;
ans--; // 答案减少
}
}
}
}
printf("%lld", ans);
return 0;
}