BZOJ2215: [Poi2011]Conspiracy
BZOJ2215: [Poi2011]Conspiracy
https://lydsy.com/JudgeOnline/problem.php?id=3832
分析:
- 这题好题啊。
- 由观察得,假设有一个合法方案想要变成另一个合法方案。这个合法方案可以用\(2sat\)求。
- 不能同时将\(2\)个或以上的同类型人换到对面去。
- 因此枚举所有情况判断是否能换过去。
- 设\(S\)为完全图集合,\(T\)为独立集合。
- 考虑:
-
- \(S\)换一个到\(T\)。
-
- \(T\)换一个到\(S\)。
-
- \(S\)和\(T\)互换一个。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
#define N 10050
template <typename T> void chkmax(T &x,T y) {if(x<y) x=y;}
template <typename T> void chkmin(T &x,T y) {if(y<x) x=y;}
int n;
bool G[N][N],g[N>>1][N>>1];
int dfn[N],low[N],S[N],ins[N],scc,bl[N],ln,fa[N],tp;
int tot[N],h[N];
void tarjan(int x) {
int i;
dfn[x]=low[x]=++dfn[0]; S[++tp]=x; ins[x]=1;
for(i=1;i<=ln;i++) if(G[x][i]) {
if(!dfn[i]) {
tarjan(i);
chkmin(low[x],low[i]);
}else if(ins[i]) chkmin(low[x],dfn[i]);
}
if(dfn[x]==low[x]) {
scc++; int t=0;
while(t!=x) {
t=S[tp--]; ins[t]=0; bl[t]=scc;
}
}
}
int main() {
scanf("%d",&n); ln=n<<1;
int i,x,y;
for(i=1;i<=n;i++) {
scanf("%d",&x);while(x--)scanf("%d",&y),g[i][y]=1;
}
int j;
for(i=1;i<=n;i++) {
for(j=i+1;j<=n;j++) {
if(g[i][j]) {
G[i+n][j]=1;
G[j+n][i]=1;
}else {
G[i][j+n]=1;
G[j][i+n]=1;
}
}
}
for(i=1;i<=ln;i++) if(!dfn[i]) tarjan(i);
for(i=1;i<=n;i++) {
if(bl[i]==bl[i+n]) {
puts("0"); return 0;
}
fa[i]=bl[i]>bl[i+n];
}
int ns=0,nt=0;
for(i=1;i<=n;i++) {
if(!fa[i]) ns++;
else nt++;
}
int ans=ns&&nt;
for(i=1;i<=n;i++) {
for(j=1;j<=n;j++) {
if(g[i][j]&&fa[i]!=fa[j]) {
tot[i]++;
h[i]=j;
}
}
if(!fa[i]) {
if(tot[i]==0&&ns>1) ans++;
}else {
if(tot[i]==ns&&nt>1) ans++;
else {
for(j=1;j<=n;j++) if(!g[i][j]&&fa[i]!=fa[j]) {h[i]=j; break;}
}
}
}
for(i=1;i<=n;i++) if(fa[i]==0) {
for(j=1;j<=n;j++) if(fa[j]==1) {
if((tot[i]==0|| (tot[i]==1&&h[i]==j) ) && ((tot[j]==ns|| (tot[j]==ns-1&&h[j]==i)) ) )ans++;
}
}
printf("%d\n",ans);
}