洛谷 P3513 [POI2011]KON-Conspiracy(2-sat方案数)

传送门


解题思路

求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;
}
上一篇:[机房测试]信息拦截


下一篇:0121-买卖股票最佳时机