题解: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;
}
posted @ 2025-08-12 18:06  ImNot6Dora  阅读(13)  评论(0)    收藏  举报