• # 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
    ",dsr);
    	return 0;
    }
    
  • 相关阅读:
    80386寄存器
    删除 Windows 旧 OS 加载器
    [C#] Socket 通讯,一个简单的聊天窗口小程序
    [erl] erlang 进程注册和注销
    VB中 '&' 和 '+' 号的区别
    如何成为一个牛逼的程序员
    [VB] if 判断语句 和 If、IIf函数的比较
    C#中通过反射方法获取控件类型和名称
    薪资至少10K的一道题,你能拿下吗
    Jass 技能模型定义(—):半人马酋长的反击光环
  • 原文地址:https://www.cnblogs.com/dsrdsr/p/10490147.html
Copyright © 2020-2023  润新知