洛谷 P3513 [POI2011]KON-Conspiracy(2-sat方案數)
阿新 • • 發佈:2021-10-19
傳送門
解題思路
求2-sat合法的方案數。
做法:
先基操判斷有無解,並求出一組解。
然後考慮兩個集合的人能否過去。
我們發現只有三種情況:A到B一個人,B到A一個人,AB交換一個人。
所以就 \(O(N^2)\) 判斷AB集合中的人是否符合條件,最後方案數即為(A到B符合的人數+1)*(B到A符合的人數+1)+互換符合的情況數。
注意2個人的情況需要特判,因為這既滿足乘法也滿足加法。
AC程式碼
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<queue> #include<stack> #include<map> #include<bitset> using namespace std; template<class T>inline void read(T &x) { x=0;register char c=getchar();register bool f=0; while(!isdigit(c))f^=c=='-',c=getchar(); while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar(); if(f)x=-x; } template<class T>inline void print(T x) { if(x<0)putchar('-'),x=-x; if(x>9)print(x/10); putchar('0'+x%10); } const int maxn=10005; stack<int> s; int p[maxn],cnt,cntt[maxn],anss,ans[maxn],ans1,ans2,times,dfn[maxn],low[maxn],a[maxn],n,in[maxn],cnt_scc; bool vis[maxn][maxn]; struct node{ int v,next; }e[maxn*maxn/2]; void insert(int u,int v){ cnt++; e[cnt].v=v; e[cnt].next=p[u]; p[u]=cnt; } void dfs(int u){ dfn[u]=low[u]=++times; s.push(u); for(int i=p[u];i!=-1;i=e[i].next){ int v=e[i].v; if(!dfn[v]){ dfs(v); low[u]=min(low[u],low[v]); }else{ if(!in[v]) low[u]=min(low[u],dfn[v]); } } if(dfn[u]==low[u]){ cnt_scc++; while(!s.empty()){ int x=s.top();s.pop(); in[x]=cnt_scc; if(x==u) break; } } } int main(){ memset(p,-1,sizeof(p)); read(n); for(int i=1;i<=n;i++){ int m; read(m); for(int j=1;j<=m;j++){ int x; read(x); vis[i][x]=1; } for(int j=1;j<=n;j++){ if(i==j) continue; if(vis[i][j]){ insert(i+n,j); }else{ insert(i,j+n); } } } for(int i=1;i<=2*n;i++) if(!in[i]) dfs(i); for(int i=1;i<=n;i++){ if(in[i]&&in[i+n]&&in[i]==in[i+n]){ cout<<0<<endl; return 0; } ans[i]=(in[i]<in[i+n]); } for(int i=1;i<=n;i++){ if(ans[i]==1){ for(int j=1;j<=n;j++){ if(i==j||ans[j]==1) continue; if(vis[i][j]) cntt[i]++,a[i]=j; } } if(ans[i]==0){ for(int j=1;j<=n;j++){ if(i==j||ans[j]==0) continue; if(!vis[i][j]) cntt[i]++,a[i]=j; } } } for(int i=1;i<=n;i++){ if(ans[i]==1){ if(cntt[i]==0) ans1++; else if(cntt[i]==1&&cntt[a[i]]==0) anss++; } if(ans[i]==0){ if(cntt[i]==0) ans2++; else if(cntt[i]==1&&cntt[a[i]]==0) anss++; } } anss+=(ans1+1)*(ans2+1); print((n==2?anss-1:anss)); return 0; }