BZOJ2215[Poi2011]Conspiracy——2-SAT+tarjan缩点
题目描述
Byteotia的领土被占领了,国王Byteasar正在打算组织秘密抵抗运动。国王需要选一些人来进行这场运动,而这些人被分为两部分:一部分成为同谋者活动在被占领区域,另一部分是后勤组织在未被占领的领土上运转。但是这里出现了一个问题: 1. 后勤组织里的任意两人都必须是熟人,以促进合作和提高工作效率。 2. 同谋者的团体中任意两人都不能是熟人。 3. 每一部分都至少要有一个人。国王想知道有多少种分配方案满足以上条件,当然也有可能不存在合理方案。现在国王将这个问题交由你来解决!
输入
第一行一个整数n(2<=n<=5000)表示有n个人参与该抵抗运动,标号为1..n。 之后有n行,第i行的第一个数ki(0<=ki<=n-1)表示i认识ki个人,随后的ki个数表示i的熟人。 p.s.输入满足:如果i是x的熟人,x会在i的序列中出现同时i也会出现在x的熟人序列中。
输出
符合条件的方案总数。
样例输入
2 2 3
2 1 3
3 1 2 4
1 3
样例输出
提示
Hint 1和4分到同谋者组织,2和3为后勤组织。 2和4分到同谋者组织,1和3为后勤组织。 4单独分到同谋者组织,1和2、3为后勤组织。
为了方便假设后勤组织为A,同谋组织为B
用邻接矩阵存每个人之间的认识情况,如果两个人认识就不能都在B,如果两个人不认识就不能都在A,枚举每个人之间的认识情况来2-SAT连边。
因为对于任意两个人在两种不同的方案中,他们不能一种方案都在A,另一种方案都在B。
所以在找到任意一组不冲突方案后,只能通过只将A中一个人移到B中或只将B中一个人移到A中或只将A和B中的各一个人调换来得到其他方案。
对于每个人,我们预处理出另一个集合中与他冲突的人数,什么是冲突的人?假设x,y认识,x在A中,y在B中,那么y就是x的冲突的人,因为如果x移到B中与y冲突。
先来考虑交换两个人的方案数。
如果一个人x的冲突人数>=2显然他不能动。
如果一个人x的冲突人数=1且与他冲突的人y没有冲突人数或与他冲突的人y只有一个冲突人数且这个冲突的人就是x,那么他们两个可以交换。
假设A中没有冲突人数的人有a个,B中有b个,那么这些人随便交换,方案数是a*b。
再来考虑移动一个人的方案数。
如果一个人没有冲突人数且他当前所在集合不只有他一个人,那么他能移到对面集合。
最后还要判断最开始随便找的一个不冲突方案是否每个集合都有人。
#include<set> #include<map> #include<stack> #include<cmath> #include<queue> #include<vector> #include<bitset> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int n; int k,x; int m[5010][5010]; int vis[10010]; int dep[10010]; int low[10010]; int s[10010]; vector<int>q[10010]; int head[10010]; int to[25000010]; int next[25000010]; int top; int t[10010]; int v[10010]; int num; int cnt; int tot; int now; int ans; int A[5010]; int B[5010]; int a,b; int miku[10010]; int NIE[10010]; void add(int x,int y) { tot++; next[tot]=head[x]; head[x]=tot; to[tot]=y; } void tarjan(int x) { t[++top]=x; low[x]=dep[x]=++num; vis[x]=1; for(int i=head[x];i;i=next[i]) { if(!dep[to[i]]) { tarjan(to[i]); low[x]=min(low[x],low[to[i]]); } else if(vis[to[i]]) { low[x]=min(low[x],dep[to[i]]); } } if(dep[x]==low[x]) { cnt++; do { now=t[top]; top--; s[now]=cnt; vis[now]=0; } while(now!=x); } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&k); for(int j=1;j<=k;j++) { scanf("%d",&x); m[i][x]=1; } } for(int i=1;i<=n;i++) { for(int j=i+1;j<=n;j++) { if(m[i][j]) { add(i+n,j); add(j+n,i); } else { add(i,j+n); add(j,i+n); } } } for(int i=1;i<=2*n;i++) { if(!dep[i]) { tarjan(i); } } for(int i=1;i<=n;i++) { if(s[i]==s[i+n]) { printf("0"); return 0; } if(s[i]<s[i+n]) { A[++a]=i; } else { B[++b]=i; } } if(a&&b) { ans++; } for(int i=1;i<=a;i++) { for(int j=1;j<=b;j++) { if(m[A[i]][B[j]]) { NIE[A[i]]++; miku[A[i]]=B[j]; } } } for(int i=1;i<=b;i++) { for(int j=1;j<=a;j++) { if(!m[B[i]][A[j]]) { NIE[B[i]]++; miku[B[i]]=A[j]; } } } for(int i=1;i<=n;i++) { if(NIE[i]==1) { if(NIE[miku[i]]==1&&miku[i]>i&&miku[miku[i]]==i) { ans++; } else if(!NIE[miku[i]]) { ans++; } } } int t1=0; int t2=0; for(int i=1;i<=n;i++) { if(!NIE[i]) { if(s[i]<s[i+n]&&a>1) { ans++; } if(s[i]>s[i+n]&&b>1) { ans++; } if(s[i]<s[i+n]) { t1++; } else { t2++; } } } ans+=t1*t2; printf("%d",ans); }