洛谷P3513 [POI2011]KON-Conspiracy

洛谷P3513 [POI2011]KON-Conspiracy

题目描述

Byteotia的领土被占领了,国王Byteasar正在打算组织秘密抵抗运动。

国王需要选一些人来进行这场运动,而这些人被分为两部分:一部分成为同谋者活动在被占领区域,另一部分是后勤组织在未被占领的领土上运转。

但是这里出现了一个问题:

1、后勤组织里的任意两人都必须是熟人,以促进合作和提高工作效率。
2、同谋者的团体中任意两人都不能是熟人。
3、每一部分都至少要有一个人。国王想知道有多少种分配方案满足以上条件,当然也有可能不存在合理方案。

现在国王将这个问题交由你来解决!

分析

如果没有输出方案数,那么这一道题就是一个裸的\(2-SAT\)问题

我们将一个点拆成两个点

其中编号为\(1-n\)的代表后勤,编号为\(n+1-2n\)的代表同谋

如果\(i\)\(j\)是熟人,那么我们从\(i+n\)\(j\)建一条边

如果\(i\)\(j\)不是熟人,那么我们从\(i\)\(j+n\)建一条边

我们按照正常的流程跑一个\(Tarjan\)就可以了

方案数为\(0\)的情况比较好求,即出现\(shuyu[i]=shuyu[i+n]\)的情况

对于有解的情况,我们要分类讨论

首先我们将所有的点分成两个集合,一个集合为后勤,另一个集合为同谋

对于后勤中的某个点,如果他和同谋中的某个点是熟人,那么我们就不能将该点加入同谋的集合

同样地,对于同谋中的某个点,如果他和后勤中的某个点不是熟人,那么我们就不能将该点加入后勤的集合

我们将这样的点称为冲突点

我们对于每一个点,都找出它的所有冲突点

一个显然的结论是,我们不能从一个集合移动两个点到达另一个集合

这样必定会产生冲突

因为如果我们将后勤集合中的两个点扔到同谋集合,那么同谋集合会出现熟人,反之亦然

因此,我们每次最多只能改变一个点的位置

因此,对于冲突点的数量大于\(2\)的节点,我们不去考虑它

如果某一个节点的冲突点的数量为\(1\),那么我们可以把该节点的冲突点拿到当前节所在的集合

前提是该节点的冲突点的冲突点的数量为\(0\)

如果节点的冲突点的数量为\(0\),那么我们可以将其扔到另一个集合中

同时,如果处在不同集合的两个点的冲突数量都为\(0\),我们可以将这两个点交换,我们用乘法原理解决即可

代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=1e6+5,maxm=5e3+5,maxk=3e7+5;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
struct asd{
	int to,next;
}b[maxk];
int head[maxn],tot=1;
void ad(int aa,int bb){
	b[tot].to=bb;
	b[tot].next=head[aa];
	head[aa]=tot++;
}
int dfn[maxn],low[maxn],dfnc,sta[maxn],top,shuyu[maxn],js;
bool vis[maxm][maxm];
void tar(int xx){
	dfn[xx]=low[xx]=++dfnc;
	sta[++top]=xx;
	for(int i=head[xx];i!=-1;i=b[i].next){
		int u=b[i].to;
		if(!dfn[u]){
			tar(u);
			low[xx]=min(low[xx],low[u]);
		} else if(!shuyu[u]){
			low[xx]=min(low[xx],dfn[u]);
		}
	}
	if(low[xx]==dfn[xx]){
		js++;
		while(1){
			int y=sta[top--];
			shuyu[y]=js;
			if(y==xx) break;
		}
	}
}
int hq[maxn],tm[maxn],jlhq,jltm,ctd[maxn],mat[maxn];
bool istm[maxn];
int main(){
	memset(head,-1,sizeof(head));
	int n;
	n=read();
	for(int i=1;i<=n;i++){
		int t;
		t=read();
		for(int j=1;j<=t;j++){
			int aa;
			aa=read();
			vis[i][aa]=1;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(i==j) continue;
			if(vis[i][j]) ad(i+n,j);
			else ad(i,j+n);
		}
	}
	for(int i=1;i<=n*2;i++){
		if(!dfn[i]) tar(i);
	}
	for(int i=1;i<=n;i++){
		if(shuyu[i]==shuyu[i+n]){
			printf("0\n");
			exit(0);
		} else if(shuyu[i]<shuyu[n+i]){
			hq[++jlhq]=i;
		} else {
			tm[++jltm]=i;
			istm[i]=1;
		}
	}
	int ans=(jlhq&&jltm),tmp1=0,tmp2=0;
	for(int i=1;i<=jlhq;i++){
		for(int j=1;j<=jltm;j++){
			if(vis[hq[i]][tm[j]]){
				++ctd[hq[i]];
				mat[hq[i]]=tm[j];
			}
		}
	}
	for(int i=1;i<=jltm;i++){
		for(int j=1;j<=jlhq;j++){
			if(!vis[tm[i]][hq[j]]){
				++ctd[tm[i]];
				mat[tm[i]]=hq[j];
			}
		}
	}
	for(int i=1;i<=n;i++){
		if(ctd[i]==1){
			if(ctd[mat[i]]==0) ans++;
		}
	}
	for(int i=1;i<=n;i++){
		if(ctd[i]==0){
			if((istm[i] && jltm>1) || (!istm[i] && jlhq>1)) ans++;
			if(istm[i]) tmp1++;
			else tmp2++;
		}
	}
	printf("%d\n",ans+tmp1*tmp2);
	return 0;
}

posted @ 2020-08-08 11:14  liuchanglc  阅读(275)  评论(3编辑  收藏  举报