Luogu P3513 [POI2011]KON-Conspiracy

题链1
题链2

分析

把图E分成A和B,其中A是子团,B是独立集
显然想到2-set,把一个点裂成两个,分别表示在A和在B
但2-set只能判断是否存在解,不能判断解的数量
所以观察发现:如果想产生新的解,必然是A,B间发生1个点的变化(反证)
所以,分类

  • A给B点u,则要满足u与B无连点
  • B给A点v,则v与A无不连点
  • A,B交换(u,v),则u与B最多有连点v,v与A最多有不连点u
    所以用b数组记录A中每个点与B的连点个数和编号,B中每个点与A的不连点个数和编号即可
    其中2-set判解是通过tarjan缩点判是否有在A的状态和在B的状态在一个SCC中,有则无解,反之无
    构造一组解,可以想缩点后建反图拓扑排序,而SCC的标号是反拓扑序,所以直接判co[i]即可
#include<bits/stdc++.h>
using namespace std;

inline int rd() {
	char ch=getchar(); int ret=0;
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') {
		ret=(ret<<1)+(ret<<3)+ch-'0';
		ch=getchar();
	}
	return ret;
}

const int N=10005,M=5005*5005;
int to[M],nxt[M],cnt,n,he[N],low[N],dfn[N],st[N],top,co[N],col,b[N],fl[N];
bool a[5005][5005];

inline void add(int u,int v) {
	to[++cnt]=v,nxt[cnt]=he[u],he[u]=cnt;
}

void tar(int u) {
	low[u]=dfn[u]=++cnt; st[++top]=u;
	for(int e=he[u];e;e=nxt[e]) {
		int v=to[e];
		if(!dfn[v]) {
			tar(v),low[u]=min(low[v],low[u]);
		} else if(!co[v]) low[u]=min(low[u],dfn[v]);
	}
	if(low[u]==dfn[u]) {
		col++;
		co[u]=col;
		while(st[top]!=u) {
			co[st[top]]=col; top--;
		}
		top--;
	}
}

int main() {
	freopen("conspiracy.in","r",stdin);
	freopen("conspiracy.out","w",stdout);
	n=rd();
	for(int i=1;i<=n;i++) {
		int k=rd();
		while(k--){
			a[i][rd()]=1;
		}
	}
	for(int i=1;i<=n;i++) {
		for(int j=1;j<i;j++) {
			if(a[i][j]) {
				add(i+n,j),add(j+n,i);
			} else {
				add(i,j+n),add(j,i+n);
			}
		}
	}
	cnt=0;
	for(int i=1;i<=n+n;i++) {
		if(!dfn[i]) tar(i);
	}
	for(int i=1;i<=n;i++) {
		if(co[i]==co[i+n]) {
			puts("0"); return 0;
		}
	}
	int Sa=0,Sb=0;
	for(int i=1;i<=n;i++) {
		fl[i]=co[i]>co[i+n];
		if(fl[i]) Sb++; else Sa++;
	}
	for(int i=1;i<=n;i++) {
		if(!fl[i]) {
			for(int j=1;j<=n;j++) {
				if(fl[j]&&a[i][j]) {
					if(!b[i]) b[i]=j;
						else b[i]=-1;
				}
			}
		} else {
			for(int j=1;j<=n;j++) {
				if(!fl[j]&&!a[i][j]) {
					if(!b[i]) b[i]=j;
						else b[i]=-1;
				}
			}
		}
	}
	int ans=0;
	if(Sa&&Sb) ans++;
	for(int i=1;i<=n;i++) {
		if(!b[i])  {
			if(fl[i]&&Sb>1) ans++; 
			if(!fl[i]&&Sa>1) ans++;
		}
	}
	if(Sa&&Sb) {
		for(int i=1;i<=n;i++) {
			for(int j=1;j<i;j++) {
				if(fl[i]^fl[j]&&(b[i]==j||b[i]==0)&&(b[j]==i||b[j]==0)) {	
					ans++;	
				}
			}
		}
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2021-02-28 16:32  wwwsfff  阅读(71)  评论(0)    收藏  举报