题解 题
这题主要的难点在于题意的转化
直接理解是要求是否存在一种方案,使i,j都能存活下来
但这样需要遍历所有可能的方案
发现n很小,我们可以\(n^2\)的扫一遍所有可能的苹果对
那问题就可以转化为check两个苹果能否共存
如果我已经知道了哪些苹果必须活下来
那check方案的时候就可以判不合法了
但如果从前往后\(O(n)\)的扫一遍方案,会有两个都能选的,仍然需要爆搜
发现我们这样爆搜,仍然是在找出一个具体的合法方案
考虑一个方案合法的必要条件是什么
当前的这个苹果确实可以随便选,但我要求它后面不能再被选
所以可以倒序枚举,先选中必须存活的苹果,然后倒序枚举人
构建出一个使这个苹果存活所必须选择的苹果集合
这样后面必须选的苹果已经确定,那些随意选的苹果对后面的影响也就能固定了
对于一对\((u, v)\), 如果后面两个都没被锁定,那它就不属于「使目标苹果存活所必须选择的苹果集合」
如果其中一个已经被锁定,那在这里就必须选另一个了
如果两个都已经被锁定,那目标苹果肯定是不能活下来的
发现对于每个苹果都能构建出这样一个集合
那就没必要先枚举苹果对了
对每个可以存活的苹果分别构建一个必要前提集合
一对\((u, v)\)能共存,当且仅当它们互不在对方的前提集合中,
且它们的前提集合没有交集(否则说明存在一对\((u, v_1)\)和\((u, v_2)\)都必须选\(u\),也是不合法情况)
这题把题意转化为求给定元素的「前提集合」的思路挺神奇的
Code:
AC代码居然这么短
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
#define ld long double
#define usd unsigned
#define ull unsigned long long
//#define int long long
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
char buf[1<<21], *p1=buf, *p2=buf;
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, m;
int u[N], v[N], f[410];
bitset<410> s[410];
signed main()
{
#ifdef DEBUG
freopen("1.in", "r", stdin);
#endif
int ans=0;
n=read(); m=read();
for (int i=1; i<=m; ++i) u[i]=read(), v[i]=read();
for (int i=1; i<=n; ++i) {
f[i]=1; s[i][i]=1;
for (int j=m; j; --j) {
if (s[i][u[j]]&&s[i][v[j]]) {f[i]=0; break;}
else if (s[i][u[j]]) s[i][v[j]]=1;
else if (s[i][v[j]]) s[i][u[j]]=1;
}
}
for (int i=1; i<=n; ++i) if (f[i])
for (int j=i+1; j<=n; ++j) if (f[j])
if (!(s[i]&s[j]).any()) ++ans;
printf("%d\n", ans);
return 0;
}