洛谷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;
}