bzoj2215[POI2011]Conspiracy

题意

给出n个点的一个图,要求把所有点分成两部分,一部分是一个团,一部分是一个独立集.n<=5000

分析

记录自己的愚蠢...
首先考虑如何判断存在可行方案,那么每个点要么属于团,要么属于独立集,且可以根据一个点属于哪一部分推出其他某些属于哪一部分.(如果我们钦定点A属于团,那么所有和A没有连边的点都必须属于独立集,如果我们钦定点A属于独立集,那么所有和A有连边的点都必须属于团).这样的二元关系相互推导的模型自然可以转化为2-SAT.点数5000,边数5000*5000,所以需要线性的做法,跑tarjan强联通分量.
然后我就不会怎么统计方案了,愚蠢*1
看题解,发现有个很妙的性质:假如我们有两个不同的方案都是合法的,那么不可能存在两个不同的点u,v使得这两个点在一个方案中都在独立集里,在另一个方案中都在团里.正确性是显然的,然而没有想到.这个结论虽然简单却非常有力:只需要找出任意一个可行解,那么其他可行解都可以由这个可行解进行次数不多的修改得到.(有三种情况:把独立集中的一个点拿到团里;把团里的一个点拿到独立集里;把团里的一个点和独立集里的一个点互换),那么我们枚举所有可能的修改情况即可.
然后我就把tarjan求强连通分量写错了.把x出栈后,我把in_stack[x]赋值为true...(应当赋值为false).愚蠢*2
过了样例交一发,然后WA了.愚蠢*3
发现有个if里的判断条件少加了一个!,意思全反了.改过来交,又WA了.愚蠢*4
发现我求出来的方案不一定两个集合都非空.改过来交,又WA了.愚蠢*5
发现我默认2SAT求出来的初始解两个集合都非空.改过来交,终于A了.
这做题状态不行啊...

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn=10005,maxm=25000005;
struct edge{
  int to,next;
}lst[maxm],lst2[maxm];int len=1,first[maxn],len2=1,first2[maxn];
void addedge(int a,int b){
  lst[len].to=b;lst[len].next=first[a];first[a]=len++;
}
void addedge2(int a,int b){
  lst2[len2].to=b;lst2[len2].next=first2[a];first2[a]=len2++;
}
int conv(int a,int t){
  return (a<<1)|t;
}//conv(a,1):a is spy conv(a,0):a is supply
int n;
bool E[maxn][maxn];
int dfn[maxn],T,stk[maxn],top,low[maxn],belong[maxn],tot;
bool ins[maxn];
vector<int> scc[maxn];
void dfs(int x){
  dfn[x]=low[x]=++T;ins[stk[top++]=x]=true;
  for(int pt=first[x];pt;pt=lst[pt].next){
    if(!dfn[lst[pt].to]){
      dfs(lst[pt].to);
      if(low[lst[pt].to]<low[x])low[x]=low[lst[pt].to];
    }else if(ins[lst[pt].to]&&dfn[lst[pt].to]<low[x])low[x]=dfn[lst[pt].to];
  }
  if(dfn[x]==low[x]){
    ++tot;
    do{
      ins[stk[--top]]=false;
      belong[stk[top]]=tot;
      scc[tot].push_back(stk[top]);
    }while(stk[top]!=x);
  }
}
bool twosat(){
  for(int i=2;i<=2*n+1;++i){
    if(!dfn[i])dfs(i);
  }
  for(int i=1;i<=n;++i){
    if(belong[conv(i,0)]==belong[conv(i,1)])return false;
  }
  return true;
}
int col[maxn],scccol[maxn];
bool mark[maxn];
bool toposorted[maxn];
int seq[maxn],cnt;
void toposort(int x){
  if(toposorted[x])return;
  for(int pt=first2[x];pt;pt=lst2[pt].next){
    toposort(lst2[pt].to);
  }
  seq[++cnt]=x;toposorted[x]=true;
}
void getsol(){
  for(int i=2;i<=2*n+1;++i){
    for(int pt=first[i];pt;pt=lst[pt].next){
      if(belong[i]!=belong[lst[pt].to]){
	addedge2(belong[i],belong[lst[pt].to]);
      }
    }
  }
  for(int i=1;i<=tot;++i)toposort(i);
  for(int i=1;i<=tot;++i){
    int x=seq[i];
    if(mark[x]&&scccol[x]==0)continue;
    mark[x]=true;scccol[x]=1;
    for(vector<int>::iterator pt=scc[x].begin();pt!=scc[x].end();++pt){
      mark[belong[(*pt)^1]]=true;scccol[belong[(*pt)^1]]=0;
    }
  }
}
int cnt_anti[maxn],anti[maxn];
int main(){
  scanf("%d",&n);
  for(int i=1,num,x;i<=n;++i){
    scanf("%d",&num);
    while(num--){
      scanf("%d",&x);E[i][x]=true;
    }
  }
  for(int i=1;i<=n;++i){
    for(int j=i+1;j<=n;++j){
      if(E[i][j]){
	addedge(conv(i,1),conv(j,0));
	addedge(conv(j,1),conv(i,0));
      }else{
	addedge(conv(i,0),conv(j,1));
	addedge(conv(j,0),conv(i,1));
      }
    }
  }
  if(!twosat())printf("0\n");
  else{
    getsol();
    for(int i=1;i<=n;++i)col[i]=scccol[belong[conv(i,1)]];
    //for(int i=1;i<=n;++i)printf("%d",col[i]);
    for(int i=1;i<=n;++i){
      if(!col[i]){
	for(int j=1;j<=n;++j){
	  if(col[j]&&E[i][j]){
	    anti[i]=j;cnt_anti[i]++;
	  }
	}
      }else{
	for(int j=1;j<=n;++j){
	  if((!col[j])&&(!E[i][j])){
	    anti[i]=j;cnt_anti[i]++;
	  }
	}
      }
    }
    int num[2]={0,0};
    for(int i=1;i<=n;++i)num[col[i]]++;
    int ans=(num[0]!=0&&num[1]!=0);
    for(int i=1;i<=n;++i){
      if(cnt_anti[i]==0&&num[col[i]]!=1)ans++;
    }
    for(int i=1;i<=n;++i){
      for(int j=1;j<=n;++j){
	if(col[i]&&(!col[j])){
	  if(cnt_anti[i]==0||(cnt_anti[i]==1&&anti[i]==j)){
	    if(cnt_anti[j]==0||(cnt_anti[j]==1&&anti[j]==i))ans++;
	  }
	}
      }
    }
    printf("%d\n",ans);
  }
  return 0;
}

posted @ 2017-07-12 21:14  liu_runda  阅读(372)  评论(0编辑  收藏  举报
偶然想到可以用这样的字体藏一点想说的话,可是并没有什么想说的. 现在有了:文化课好难