题目描述
题解:
这个题目要求的是把一张无向图变成一个团和一个独立集的方案数。这看似好像无从下手。那么我们可以换一个角度思考,我们考虑先求出一个解,然后通过调整得出所有解。
假设已经求出了一个解,我们会发现,其它所有的解只可能由这个解通过三种方式得到:
1.将一个原本在独立集里面的点放到团中(可行的条件下)。
2.将一个原本在团中的点放到独立集中(可行的条件下)。
3.将一个独立集中的点和团中的点交换(可行的条件下)。
意思就是,如果有属于同一组中的两个及以上的点到另一个组中,就肯定不行。
那么接下来问题就转化成了怎样求一组解。我们可以把一个点拆分成两个状态,放入团或是放入独立集中。那么我们的目标即是将这2n个状态分到两个集合中,每个集合中有n个状态,此时我们随便选择一个集合,就是一组可行解了。
那么我们就可以用2-Sat来解决。
约束条件是如果a到b有边,那么a在独立集中,b就不能在。
如果a到b无边,那么a在团中,b就不能在。
剩下的过程就是Tarjan缩点加上建反图拓扑排序一下。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int maxn=10005;
int n;
namespace twosat{
vector<int> e[maxn],a[maxn],f[maxn];
bool vis[maxn];
bitset<5005> hsh[5005];
int tot,top,ans,inn[maxn],c[maxn],dfn[maxn],low[maxn],gr[maxn],stack[maxn],que[maxn];
bool pic[maxn],pd[maxn],pd1[maxn];
void nosolu(){printf("0\n"); exit(0);}
void init(){
scanf("%d",&n);
for (int i=1;i<=n;i++){
int x,y; scanf("%d",&x);
for (int j=1;j<=x;j++) scanf("%d",&y),hsh[i][y]=1;
for (int j=1;j<=n;j++)
if (j!=i){
if (hsh[i][j]) e[2*i].push_back(2*j-1);//2*i-1是团,2×i是独立集
else e[2*i-1].push_back(2*j);
}
}
if (n==2) {printf("2\n"); exit(0);}
}
void tarjan(int x){
dfn[x]=low[x]=++tot,vis[x]=1,stack[++top]=x;
for (int j=0;j<e[x].size();j++)
if (!dfn[e[x][j]]) {
int y=e[x][j];
tarjan(y);
low[x]=min(low[x],low[y]);
} else if (vis[e[x][j]]) low[x]=min(low[x],dfn[e[x][j]]);
if (low[x]==dfn[x]){
while (stack[top]!=x) {gr[stack[top]]=x,vis[stack[top]]=0,top--;}
gr[stack[top]]=x,vis[stack[top]]=0,top--;
}
}
void tar(){for (int i=1;i<=2*n;i++) if (!dfn[i]) tarjan(i);}
int get(int x){if (x%2==1) return x+1; else return x-1;}
void rebuild(){
for (int i=1;i<=2*n;i++) f[gr[i]].push_back(i);
for (int i=1;i<=n;i++) if (gr[2*i-1]==gr[2*i]) nosolu();
for (int i=1;i<=2*n;i++){
for (int j=0;j<e[i].size();j++)
if (gr[i]!=gr[e[i][j]]) a[gr[e[i][j]]].push_back(gr[i]),inn[gr[i]]++;
e[i].clear();
}
}
void topsort(){
memset(vis,0,sizeof(vis));
int head=0,tail=0;
for (int i=1;i<=2*n;i++)
if (!inn[i]) que[++tail]=i;
while (head!=tail){
head++; int u=que[head];
for (int i=0;i<f[u].size();i++)
if (!vis[get(f[u][i])]) pic[f[u][i]]=1,vis[f[u][i]]=1;
f[u].clear();
for (int i=0;i<a[u].size();i++)
if (inn[a[u][i]]){
int v=a[u][i]; inn[v]--;
if (!inn[v]) que[++tail]=v;
}
a[u].clear();
}
}
void calc(){
int ans=0,sum1=0,sum2=0;
for (int i=1;i<=n;i++)
if (pic[2*i-1]) c[i]=1,sum1++; else c[i]=2,sum2++; //1是团,2是独立集
if (sum1&&sum2) ans++;
for (int i=1;i<=n;i++)
if (c[i]==1){
bool check=0;
for (int j=1;j<=n;j++)
if (hsh[i][j]&&c[j]==2) {check=1; break;}
if (check==0&&(sum1-1)) ans++,pd[i]=1;
} else {
bool check=0;
for (int j=1;j<=n;j++)
if (c[j]==1&&(!hsh[i][j])) {check=1; break;}
if (check==0&&(sum2-1)) ans++,pd1[i]=1;
}
for (int i=1;i<n;i++)
for (int j=i+1;j<=n;j++)
if (((pd[i]&&pd1[j])||(pd1[i]&&pd[j]))&&hsh[i][j])
ans++;
printf("%d\n",ans);
}
void solve(){
init(); tar(); rebuild(); topsort(); calc();
}
}
int main(){
twosat::solve();
return 0;
}