# 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;
}
全部评论

相关推荐

不知名bang:感觉三个项目可以融在一起,比如上层是用手写的epoll,然后到tcp聊天层,然后你写了一个后台监控(不过我也不懂c++,但是感觉写一个大项目比三个小项目要好)
我的求职进度条
点赞 评论 收藏
分享
饥饿的长颈鹿就要上岸...:简历五项结构 简历只放五项内容,顺序和格式如下: 一、个人信息 只写名字、电话、邮箱 不写性别、年龄、籍贯、政治面貌、微信等额外信息 二、教育经历 格式:学校名称 | 学历 | 专业 | 就读时间 从左到右排列,一行写完 如果专业和岗位对口,写1-2行主修课程;不对口就不写 学历如果不占优势,可以把教育经历放到简历靠后的位置 三、实习/项目经历 如果没有实习经历,全部写项目经历 每条经历格式:项目名 + 岗位名 + 任职时间段 下面写三到五条工作内容 每条工作内容开头必须用四个字概括,加粗,后面跟一条完整描述 所有描述必须用STAR法则来写(情境-任务-行动-结果) 每一条都要有数据支撑和具体成果 四、个人优势 可以写获得的奖项、证书 如果奖项不够,就写你熟练掌握的技能 每条也要有具体数据或成果支撑,不能空泛堆砌 五、整体要求 一页纸,不要超过一页 个人信息只写名字加电话邮箱 贝贝试一下这个方式写简历,我虽然没收到offer,至少收到了好几轮面试
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务