题:Poor Turkeys
AtCoder 2389 洛谷:AT2389
题目大意:存在N只鸡,M个人,每个人轮流指定两只鸡,选择杀里面存活的一只(都死则不动),求最后可能存在的鸡对。
分析:此题如果正向枚举会进入分叉还要结合其他条件判断,故不能直接正向做。但因为M很大N较小,那么我们就考虑复杂度更低的枚举答案,即枚举鸡对判断能否成立,而枚举的复杂度则为
,接下来我们就要考虑如何使鸡对成立,我们先从简单的分析一只鸡的存活条件开始。
单独分析鸡的存活条件
我们发现如果想要一个鸡 i 存活,如果它从来没有被指定,那肯定成立,但如果被指定过,那么就需要保证在指定的那一步时,杀掉另外一只鸡。这样我们就找到了”鸡的传火“——你想要一只鸡存活的条件是另一只鸡存活。然后我们又可以根据另一只鸡的条件进行判断......这样,我们最终根据这个传火就能O(m)判断 i 的存活条件,n只鸡在一起则为O(mn)并且由于是倒推存活,故为可称之为时间倒流。
我们接下来思考如何存储存活条件,由于直接涉及的对象是鸡,故我们考虑用一个bitset存储两只鸡间存活关系的影响,我们就考虑直接情况,即s[i][j]表示想要i存活需不需要j存活。
首先初始化,即首先需要自己本身存活,然后举每个人的指认u,v,如果
那么
,此处也可以直接判断i的死活:如果
,那么 i 今天biss
//a1[i],指认第一个,a2[i],指认第二个
for(int i = 1;i <= n; i++) s[i][i] = 1;
for(int i = 1;i <= n; i++)
{
for(int j = m;j >= 1; j--)//时光倒流
{
if(s[i][a1[j]] and s[i][a2[j]])//s[i][j] = 1——要让j死保护i的情况,a1[j]和a2[j]如果都要保护i则不成立(其中一个一定会死)
{
dead[i] = 1;
break;
}
if(s[i][a1[j]] or s[i][a2[j]])//只是其中一个要进行保护,则另一个也不能先死
s[i][a1[j]] = s[i][a2[j]] = 1;
}
}
分析鸡对的成立情况
接下来再回到鸡对的问题上,存在鸡对的情况为:
1 两只鸡本身都存活 即:dead[i] = dead[j] = 0;
2 两只鸡的存活set无交 即: s[i]&s[j] =0()
最后 枚举即可
for(int i = 1;i <= n; i++)
{
if(dead[i]) continue;
for(int j = i + 1;j <= n; j++)
{
if(dead[j]) continue;
if(!(s[i]&s[j]).count())//交集为空则成立
ans++;
}
}
另:由于鄙人蒟蒻,故在此稍微总结一点bitset的用法
1 bitset 之间能进行进行& | ^,分别表示将两个bitset按位进行与,或,异或的操作
2 s.count()可以return 1 的个数
完整代码:
//9.05-9.16
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 100;
int n,m,a1[N],a2[N],d[501];
bitset<402>s[402];
int Read()
{
int x = 0,k = 1;char ch = getchar();
while(!isdigit(ch)) {if(ch == '-') k = -1;ch = getchar();}
while(isdigit(ch)) {x = (x << 3) + (x << 1) + ch - '0';ch = getchar();}
return x * k;
}
int main()
{
n = Read(),m = Read();
// scanf("%d%d",&n,&m);
for(int i = 1;i <= m; i++)
{
a1[i] = Read(),a2[i] = Read();
// scanf("%d%d",&a1[i],&a2[i]);
// s[a1[i]][a2[i]] = 1;
}
for(int i = 1;i <= n; i++) s[i][i] = 1;
for(int i = 1;i <= n; i++)
{
for(int j = m;j >= 1; j--)
{
if(s[i][a1[j]] and s[i][a2[j]])
{
d[i] = 1;
break;
}
if(s[i][a1[j]] or s[i][a2[j]])
s[i][a1[j]] = s[i][a2[j]] = 1;
}
}
long long ans = 0;
for(int i = 1;i <= n; i++)
{
if(d[i]) continue;
for(int j = i + 1;j <= n; j++)
{
if(d[j]) continue;
if(!(s[i]&s[j]).count())
ans++;
}
}
cout << ans << '\n';
return 0;
}
`