# bzoj2215: [Poi2011]Conspiracy 2-sat

bzoj2215: [Poi2011]Conspiracy 2-sat

链接

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

思路

一个点的属性为去当同谋者和后勤两种
求出一种方案来很简单(只需要用简单的2-sat)
我们发现一条特别重要的性质:
一个方案只会由已经求出的其他任意一种方案改变一方的一个人得来
(基本看出来就稳了)
因为两个人不能同时过去(显然)
那么我们先求出一种方案
然后统计ok[i],表示i到对面冲突的点的个数
显然只有几种情况(大力分情况讨论)
①.一个间谍去后勤
②.一个后勤区去间谍
就是ok==0的个数(注意双方都不能为0个人)
③.间谍和后勤互换(容易发现交换的两个人一定一个是0,一个是1)
好了。

错误

tarjan求方案的方向居然写反了、、、
还有mp的i+n没减n

代码

#include <iostream>
#include <cstdio>
#include <vector>
#define iter vector<int>::iterator
const int N=5007;
using namespace std;
int read() {
    int x=0,f=1;char s=getchar();
    for(;s>'9'||s<'0';s=getchar()) if(s=='-') f=-1;
    for(;s>='0'&&s<='9';s=getchar()) x=x*10+s-'0';
    return x*f;
}
int n,ok[N<<1],dsr;
bool mp[N][N];
vector<int> ans[2];
struct node {
    int v,nxt;
}e[N*N];
int head[N<<1],tot;
void add(int u,int v) {
    e[++tot].v=v;
    e[tot].nxt=head[u];
    head[u]=tot;
}
int dfn[N<<1],low[N<<1],stak[N<<1],top,cnt,belong[N<<1],vis[N<<1];
void tarjan(int u) {
    dfn[u]=low[u]=++cnt;
    vis[u]=1;
    stak[++top]=u;
    for(int i=head[u];i;i=e[i].nxt) {
        int v=e[i].v;
        if(!dfn[v]) {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        } else if(vis[v]) {
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(low[u]==dfn[u]) {
        belong[0]++;
        while(stak[top]!=u) {
            vis[stak[top]]=0;
            belong[stak[top]]=belong[0];
            top--;
        } top--;
        vis[u]=0;
        belong[u]=belong[0];
    }
}
int main() {
    n=read();
    for(int i=1;i<=n;++i) {
        int k=read();
        for(int j=1;j<=k;++j) mp[i][read()]=1;
    }
    for(int i=1;i<=n;++i) {
        for(int j=1;j<i;++j) {
            if(mp[i][j]) {
                add(i,j+n),add(j,i+n);
            } else {
                add(i+n,j),add(j+n,i);
            }
        }
    }
    for(int i=1;i<=n+n;++i)
        if(!dfn[i])
            tarjan(i);
    for(int i=1;i<=n;++i) {
        if(belong[i]==belong[i+n]) {
            puts("0");
            return 0;
        }
    }
    for(int i=1;i<=n;++i) {
        if(belong[i] < belong[i+n]) ans[0].push_back(i);
        else ans[1].push_back(i+n);
    }
    if(ans[0].size()&&ans[1].size()) dsr++;
    for(iter i=ans[0].begin();i!=ans[0].end();++i) {
        for(iter j=ans[1].begin();j!=ans[1].end();++j) {
            if(!mp[*i][*j-n]) ok[*i]++;     
        }
    }
    for(iter i=ans[1].begin();i!=ans[1].end();++i) {
        for(iter j=ans[0].begin();j!=ans[0].end();++j) {
            if(mp[*i-n][*j]) ok[*i]++;
        }
    }
    int siz_0[2]={};
    for(int k=0;k<=1;++k)
        for(iter i=ans[k].begin();i!=ans[k].end();++i)
            if(!ok[*i]) siz_0[k]++;
    if(ans[0].size()>1) dsr+=siz_0[0];
    if(ans[1].size()>1) dsr+=siz_0[1];
    for(iter i=ans[0].begin();i!=ans[0].end();++i) {
        if(ok[*i]==1) {
            for(iter j=ans[1].begin();j!=ans[1].end();++j) {
                if(!mp[*i][*j-n]&&!ok[*j]) dsr++;
            }
        }
    }
    for(iter i=ans[1].begin();i!=ans[1].end();++i) {
        if(ok[*i]==1) {
            for(iter j=ans[0].begin();j!=ans[0].end();++j) {
                if(mp[*i-n][*j]&&!ok[*j]) dsr++;
            }
        }
    }
    printf("%d\n",dsr);
    return 0;
}
全部评论

相关推荐

沉淀一会:**圣经 1.同学你面试评价不错,概率很大,请耐心等待;2.你的排名比较靠前,不要担心,耐心等待;3.问题不大,正在审批,不要着急签其他公司,等等我们!4.预计9月中下旬,安心过节;5.下周会有结果,请耐心等待下;6.可能国庆节前后,一有结果我马上通知你;7.预计10月中旬,再坚持一下;8.正在走流程,就这两天了;9.同学,结果我也不知道,你如果查到了也告诉我一声;10.同学你出线不明朗,建议签其他公司保底!11.同学你找了哪些公司,我也在找工作。
点赞 评论 收藏
分享
美团 后端开发 总包n(15%是股票)
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务