【AtCoder Grand Contest 016 E】Poor Turkeys
DP,倒推。
题目大意:
有 $n$ 只鸡和 $m$ 个人,每个人有 $x_i$ 和 $y_i$。
对于一个人 $i$:
- 若鸡 $x_i$ 和 $y_i$ 都活着,则等概率选择一只杀了。
- 若鸡 $x_i$ 和 $y_i$ 只有一只活着,则杀了活着的那只。
- 若鸡 $x_i$ 和 $y_i$ 都死了,则什么都不干。
人会按照编号顺序行动。
问有多少对 $(i,j)$ 满足 $i\lt j$ 且最后编号为 $i$ 和 $j$ 的鸡有可能同时存活。
题解:
我们先钦定一只鸡 $i$ 必须活着,然后尝试推出到每一步时,要使鸡最终活着,还需要哪些鸡必须活着。
考虑倒推,令 $f_{i,j}$ 表示要使鸡 $i$ 活着,鸡 $j$ 是否必须活着。
一开始的状态为 $f_{i,i}=1$。
然后倒着枚举每一个人,并考虑以下情况:
- $f_{i,x_i}$ 和 $f_{i,y_i}$ 都为 $0$,即这个人操作后这两只鸡的死活和最终状态无关。那么之前也无关。
- $f_{i,x_i}$ 和 $f_{i,y_i}$ 中的一个位 $0$。那么这个人只能杀了另一个,所以令另一个为 $1$。
- $f_{i,x_i}$ 和 $f_{i,y_i}$ 都为 $1$。那么出现矛盾,说明鸡 $i$ 不可能活到最后。
我们发现,这些死亡是有先后顺序的,所以两只鸡的集合如果有交的话,这两只鸡不能同时存活。
然后,我们枚举两只能活到最后的鸡,看下是否无交即可。可以用 bitset 优化。
时间复杂度 $O(nm+\frac{n^3}\omega)$。
Code:
#include<bitset>
#include<iostream>
using namespace std;
const int N=405;
bitset<N>f[N],g;
int n,m,U[N*N],V[N*N];
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;++i)cin>>U[i]>>V[i];
for(int i=1;i<=n;++i){
f[i].set(i);
for(int j=m;j;--j)
if(f[i].test(U[j])&&f[i].test(V[j])){g.set(i);break;}else
if(f[i].test(U[j]))f[i].set(V[j]);else
if(f[i].test(V[j]))f[i].set(U[j]);
}
int ans=0;
for(int i=1;i<=n;++i)
if(!g[i])
for(int j=i+1;j<=n;++j)
if(!g[j]&&(f[i]&f[j]).none())++ans;
cout<<ans;
return 0;
}