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();//输出答案大小
}

浙公网安备 33010602011771号