分析
设(f[i])表示第(i)个是否幸存,(dp[i][j])表示若第(i)个幸存,第(j)个是否必死
倒序枚举人,如果存在(dp[i][a[x]],dp[i][b[x]])同时为是,为了避免矛盾,第(i)个必死
如果存在(dp[i][a[x]],dp[i][b[x]])其中一个为是,那么另一个也为是
最后枚举两个点(i,j)若没有一个点(k)使得(dp[i][k],dp[j][k])同时为是,那么((i,j))幸存
代码
#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=411,M=50011;
int n,m,a[M],b[M],ans; bool f[N],dp[N][N];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
signed main(){
n=iut(),m=iut();
for (rr int i=1;i<=m;++i) a[i]=iut(),b[i]=iut();
for (rr int i=1;i<=n;++i){
f[i]=dp[i][i]=1;
for (rr int j=m;j>=1;--j){
if (!dp[i][a[j]]&&!dp[i][b[j]]) continue;
if (dp[i][a[j]]&&dp[i][b[j]]){f[i]=0; break;}
dp[i][a[j]]=dp[i][b[j]]=1;
}
}
for (rr int i=1;i<n;++i)
for (rr int j=i+1;j<=n;++j)
if (f[i]&&f[j]){
rr int flag=1;
for (rr int k=1;k<=n;++k)
if (dp[i][k]&&dp[j][k]){
flag=0; break;
}
if (flag) ++ans;
}
return !printf("%d",ans);
}