传送门
解题思路
求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;
}