BZOJ2948 : [Poi2001]綠色遊戲

Claris發表於2017-08-17

維護一個保護集合$S$,表示哪些點$A$可能勝利。

首先將所有綠點加入$S$。

$1.$對於一個不在$S$的$A$點,若它存在某個後繼在$S$中,則將其加入$S$。

$2.$對於一個不在$S$的$B$點,若它所有後繼都在$S$中,則將其加入$S$。

通過拓撲可以$O(n+m)$求出$S$集合,那麼剩下的點$A$必敗。

$1.$對於一個在$S$的$A$點,若它所有後繼都不在$S$中,則將其從$S$中移除。

$2.$對於一個在$S$的$B$點,若它存在某個後繼不在$S$中,則將其從$S$中移除。

同樣可以通過拓撲$O(n+m)$求出最終的$S$集合。

這樣會導致某些綠點不在$S$中,那麼它們就失去了作為綠點的價值,將其標記為白點。

重複執行這個演算法$O(n)$輪直到所有綠點都發揮了價值,此時$S$中的點$A$必勝。

時間複雜度$O(n(n+m))$。

 

#include<cstdio>
const int N=3010,M=30010;
int n,m,i,j,x,c[N],d[N],g[N],v[M],nxt[M],ed,q[N],vis[N],deg[N],cnt;
inline void add(int x,int y){d[v[++ed]=x]++;nxt[ed]=g[y];g[y]=ed;}
bool solve(){
  int i,x,h=1,t=0;
  for(i=1;i<=n;i++){
    vis[i]=c[i],deg[i]=d[i];
    if(vis[i])q[++t]=i;
  }
  while(h<=t)for(i=g[q[h++]];i;i=nxt[i])if(!vis[x=v[i]])
    if(x<=m)vis[q[++t]=x]=1;
    else if(!(--deg[x]))vis[q[++t]=x]=1;
  for(i=h=1,t=0;i<=n;i++){
    deg[i]=d[i];
    if(!vis[i])q[++t]=i;
  }
  while(h<=t)for(i=g[q[h++]];i;i=nxt[i])if(vis[x=v[i]])
    if(x>m)vis[q[++t]=x]=0;
    else if(!(--deg[x]))vis[q[++t]=x]=0;
  for(t=0,i=1;i<=n;i++)if(c[i]&&!vis[i])c[i]=0,t=1;
  return t;
}
int main(){
  scanf("%d%d",&m,&n);n+=m;
  for(i=1;i<=n;i++)for(scanf("%d%d",&c[i],&j);j--;add(i,x))scanf("%d",&x);
  while(solve());
  for(i=1;i<=n;i++)if(vis[i])q[++cnt]=i;
  for(printf("%d\n",cnt),i=1;i<=cnt;i++)printf("%d\n",q[i]);
  return 0;
}

  

相關文章