1. 程式人生 > 實用技巧 >洛谷P3513 [POI2011]KON-Conspiracy

洛谷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\),我們可以將這兩個點交換,我們用乘法原理解決即可

程式碼

#define fastcall __attribute__((optimize("-O3")))
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=5e3+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[maxn*maxn];
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[maxn][maxn];
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;
}