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;
}
posted @ 2024-11-24 21:03  Panda_LYL  阅读(62)  评论(0)    收藏  举报