BZOJ2215: [Poi2011]Conspiracy

BZOJ2215: [Poi2011]Conspiracy

https://lydsy.com/JudgeOnline/problem.php?id=3832

分析:

  • 这题好题啊。
  • 由观察得,假设有一个合法方案想要变成另一个合法方案。这个合法方案可以用\(2sat\)求。
  • 不能同时将\(2\)个或以上的同类型人换到对面去。
  • 因此枚举所有情况判断是否能换过去。
  • \(S\)为完全图集合,\(T\)为独立集合。
  • 考虑:
    1. \(S\)换一个到\(T\)
    1. \(T\)换一个到\(S\)
    1. \(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);
}

转载于:https://www.cnblogs.com/suika/p/10205229.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值