ABC469D The Big Two

提供一发图论题解。

题目大意

\(n\) 名玩家,共进行 \(m\) 次比赛,第 \(i\) 次比赛的两名玩家为 \(a_i,b_i\)。现统计满足以下条件的无序整数对 \((x,y)\) 的个数:

  • 对于每一次比赛,至少有一名玩家属于 \((x,y)\)

分析

将每次比赛看成一条连接两名玩家的无向边。题目就变成了删除两个点,让这个图没有边的选法数量。当我们删除顶点 \(a,b\) 时,根据容斥原理,删除的边数为 \(d_a+d_b-cnt_{a,b}\),其中 \(d\) 为度数,也就是与某个点相连的边数;\(cnt\) 为两个点之间边的数量。当删除的边数恰好与 \(m\) 相等,表明这个方案是可行解。因此我们可以通过枚举顶点 \(a,b\) 进行统计,时间复杂度 \(O(n^2)\),无法接受。

观察到,如果我们任取一条边 \(a_0,b_0\),对于任意一个可行点对 \((x,y)\)必须至少包含 \(a_0\) 或者 \(b_0\)。因此我们只需枚举所有包含 \(a_0\)\(b_0\) 的点对即可。时间复杂度 \(O(n)\),可以接受。

实现

答案记得去重,我因为懒所以用的 set。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
struct edge{
	int u,v;
}e[N];
int n,m,d[N],choseu,chosev;
unordered_map<int,int>cnt;
set<int>ans;
int ys(int u,int v){//映射,便于map存储与查询
	return 1LL*u*N+v;
}
bool check(int u,int v){//成立条件dA+dB+cnt(ys(a,b))=m
	if(u>v)swap(u,v);
	return d[u]+d[v]-cnt[ys(u,v)]==m;
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>e[i].u>>e[i].v;
		if(e[i].u>e[i].v)swap(e[i].u,e[i].v);
		if(i==1){choseu=e[i].u;chosev=e[i].v;}
		d[e[i].u]++;d[e[i].v]++;//度数+1
		cnt[ys(e[i].u,e[i].v)]++;//两点之间的边数+1
	}
	for(int i=1;i<=n;i++){//枚举所有包含u的点对
		if(i==choseu)continue;
		if(check(choseu,i)){
			int ansa=choseu,ansb=i;
			if(ansa>ansb)swap(ansa,ansb);
			ans.insert(ys(ansa,ansb));
		}
	}
	for(int i=1;i<=n;i++){//枚举所有包含v的点对
		if(i==chosev)continue;
		if(check(chosev,i)){
			int ansa=chosev,ansb=i;
			if(ansa>ansb)swap(ansa,ansb);
			ans.insert(ys(ansa,ansb));
		}
	}
	cout<<ans.size();//输出答案大小
}
posted @ 2026-08-05 15:07  UltraRT  阅读(2)  评论(0)    收藏  举报