题解:AtCoder [AGC016E] Poor Turkeys
题意
有 \(n\) 只火鸡,\(m\) 个人,每人依次选择两只火鸡,若两只火鸡都活着,那么其中一个将会死;若只有一只活着,那么这只火鸡就会死;若两只火鸡都死了那么无事发生。求有多少个二元组 \((i,j)\) 满足火鸡 \(i\) 和 \(j\) 最终可能都活着。
思路
考虑一只火鸡 \(i\) 什么情况能活到最后。若火鸡 \(i,j\) 在某一轮被选中,为了让 \(i\) 活下来,那么 \(j\) 必须死;为了让 \(j\) 能够恰好在这一轮死,那么此前 \(j\) 就必须活下来,也就是说,如果在这一轮之前的某一轮 \(j\) 与一只火鸡 \(k\) 被选中,那么为了让 \(j\) 活,\(k\) 必须死,依此类推。容易发现,对于每只火鸡 \(x\),都有一个它的“替死鬼集合”\(f_x\),这些替死鬼在它们需要死的那一轮之前都必须活下来。所以我们需要倒序处理每一轮选择,并记录所有 \(f\):每当一只火鸡 \(y\) 与火鸡 \(x\) 或者 \(f_x\) 中的一只火鸡在同一轮被选中,那么火鸡 \(y\) 也要加入 \(f_x\) 中。
我们将处理顺序固定好之后,按照倒序思维继续考虑火鸡 \(i\) 什么时候必死。沿用上面的例子,如果在 \(j,k\) 同时被选中的这轮之前,\(i,k\) 在同一轮被选中了,假设 \(i\) 能活,那么 \(k\) 就必死,而 \(k\in f_i\),所以 \(i\) 就必死。容易发现,当一只火鸡 \(y\) 加入一只火鸡 \(x\) 的 \(f_x\) 后,若火鸡 \(y\) 与 \(x\) 或 \(f_x\) 中的一只火鸡再次在同一轮被选中,那么火鸡 \(x\) 必死。所以我们在记录 \(f\) 的同时,也要用一个bool类型数组 \(g\) 记录火鸡是否必死。
最后枚举二元组 \((i,j)\),只要 \(i,j\) 都不必死且 \(S_i\cap S_j=\emptyset\),那么这个二元组就满足条件。
为了方便计算交集,可以使用bitset存储每只火鸡的替死鬼集合,按位与一下就能判断是否有交集。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+10,maxm=400;
ll n,m,ans;
bitset<maxm>f[maxm+10];
bool g[maxm+10];
int a[maxn],b[maxn];
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++)cin>>a[i]>>b[i];
for(int i=1;i<=n;i++){
for(int j=m;j>=1;j--){
if((f[i][a[j]-1]&&f[i][b[j]-1])||(a[j]==i&&f[i][b[j]-1])||(b[j]==i&&f[i][a[j]-1]))g[i]=1;
else if(f[i][a[j]-1]||a[j]==i)f[i][b[j]-1]=1;
else if(f[i][b[j]-1]||b[j]==i)f[i][a[j]-1]=1;
}
}
for(int i=1;i<=n;i++)f[i][i-1]=1;
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if(g[i]||g[j]||(f[i]&f[j]).any())continue;
ans++;
}
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号