题目大意:给定一张无向图,要求你把图中的每个点染成红色或蓝色,使得红色的点形成一个团,蓝色的点形成一个独立集
首先假设我们已经求出了一组可行解,那么其他方案至多是本方案中从后勤和同谋组各出一个人进入对方组,因为一旦从某一组出来两个就一定不满足题意
这时我们考虑处理出每个人是否只与对面集合中的一个人冲突,然后分类三种情况(只有后勤去同谋,只有同谋去后勤,两边交换一个人)枚举就可以了。
现在考虑如何求可行解
我们可以想到2SAT,对于每个点,红色和蓝色就是它的两种对立状态,然后有边的用一种限制条件,没边的用另外一种限制条件,然后tarjan缩点判冲突情况。接着给图反向,按拓扑序染色求方案即可
代码巨恶心....
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define N 10010
#define M 25000010
using namespace std;
void NO(){puts("0");exit(0);}
bool a[5010][5010];
struct ljb
{
int to[M],nxt[M],pre[N<<1],cnt;
ljb()
{
cnt=0;
memset(pre,0,sizeof(pre));
}
void ae(int ff,int tt)
{
cnt++;
to[cnt]=tt;
nxt[cnt]=pre[ff];
pre[ff]=cnt;
}
}Z,F;
int dfn[N<<1],low[N<<1],cn;
bool zai[N<<1];
int s[N<<1],t;
int b[N<<1],c;
vector<int>V[N<<1];
void tarjan(int x)
{
cn++;
low[x]=dfn[x]=cn;
zai[x]=true;
t++;s[t]=x;
int i,j;
for(i=Z.pre[x];i;i=Z.nxt[i])
{
j=Z.to[i];
if(!dfn[j])
{
tarjan(j);
low[x]=min(low[x],low[j]);
}
else if(zai[j]) low[x]=min(low[x],dfn[j]);
}
if(low[x]==dfn[x])
{
c++;
while(s[t]!=x)
{
b[s[t]]=c;
zai[s[t]]=false;
V[c].push_back(s[t]);
t--;
}
b[x]=c;
zai[x]=false;
V[c].push_back(x);
t--;
}
}
int du[N<<1];
int q[N<<1],h,col[N<<1];
int f[N];
int p1[N<<1],p0[N<<1];
int n;
int ct[N<<1];
void gaoyigao()
{
int i,j,x,y;
h=1;t=0;
for(i=1;i<=c;i++)
if(!du[i])
{
t++;
q[t]=i;
}
while(h<=t)
{
x=q[h];h++;
if(col[x]==2) continue;
col[x]=1;
for(i=0;i<V[x].size();i++)
col[b[V[x][i]^1]]=2;
for(i=F.pre[x];i;i=F.nxt[i])
{
j=F.to[i];
du[j]--;
if(du[j]==0&&!col[j])
{
t++;
q[t]=j;
}
}
}
int tot1=0,tot0=0;
for(i=1;i<=c;i++)
if(col[i]==1)
{
for(j=0;j<V[i].size();j++)
{
if(V[i][j]&1) f[V[i][j]/2]=1,tot1++,p1[tot1]=V[i][j]/2;
else f[V[i][j]/2]=0,tot0++,p0[tot0]=V[i][j]/2;
}
}
int ans=0;
for(i=1;i<=n;i++)
{
if(f[i]==1)
{
for(j=1;j<=tot0;j++)
if(!a[p0[j]][i])
{
if(!ct[i]) ct[i]=p0[j];
else ct[i]=-1;
}
}
else
{
for(j=1;j<=tot1;j++)
if(a[p1[j]][i])
{
if(!ct[i]) ct[i]=p1[j];
else ct[i]=-1;
}
}
}
if(tot0>1)
{
for(i=1;i<=tot0;i++)
if(!ct[p0[i]])
ans++;
}
if(tot1>0)
{
for(i=1;i<=tot1;i++)
if(!ct[p1[i]])
ans++;
}
if(tot0&&tot1)
{
ans++;
for(i=1;i<=tot0;i++)
for(j=1;j<=tot1;j++)
if((!ct[p0[i]]||ct[p0[i]]==p1[j])&&(!ct[p1[j]]||ct[p1[j]]==p0[i]))
ans++;
}
printf("%d",ans);
}
int main()
{
scanf("%d",&n);
int i,j,x,y;
for(i=1;i<=n;i++)
{
scanf("%d",&x);
for(j=1;j<=x;j++)
{
scanf("%d",&y);
a[i][y]=true;
}
}
for(i=1;i<=n;i++)
for(j=i+1;j<=n;j++)
{
if(a[i][j])
{
Z.ae(2*i+1,2*j);
Z.ae(2*j+1,2*i);
}
else
{
Z.ae(2*i,2*j+1);
Z.ae(2*j,2*i+1);
}
}
for(i=2;i<=2*n+1;i++)
if(!dfn[i]) tarjan(i);
for(i=2;i<=2*n;i+=2)
if(b[i]==b[i+1]) NO();
for(j=2;j<=2*n+1;j++)
for(i=Z.pre[j];i;i=Z.nxt[i])
if(b[j]!=b[Z.to[i]])
{
F.ae(b[Z.to[i]],b[j]);
du[b[j]]++;
}
gaoyigao();
}