BZOJ5412 : circle

若仅保留这$k$个点仍然有环,那么显然无解。

否则设$A$表示这$k$个点的集合,$B$表示剩下的点的集合,因为是竞赛图,每个集合内部的拓扑关系是一条链,方便起见将所有点按照在所在集合的链上的位置进行重标号。

对于$B$中每个点$i$,求出$l_i$表示最小的$j$,满足$B_i\rightarrow A_j$有边,再求出$r_i$表示最大的$j$,满足$A_j\rightarrow B_i$有边。

那么需要保留$B$中尽可能多的点,满足对于任意两个点$B_j,B_i(j\leq i)$都有$r_j<l_i$,不然就会有环,即$l_i>\max(r_j)(j\leq i)$。

考虑DP,设$f[i][j]$表示考虑$B$中前$i$个点,前面选择的点的$r$的最大值为$j$时最多可以选择几个点,暴力转移即可。

时间复杂度$O(n^2)$。

 

#include<cstdio>
const int N=2010,BUF=12000000;
char Buf[BUF],*buf=Buf;
int n,m,i,j,x,y,l[N],r[N],L,R,a[N],ca,b[N],cb,q[N],h,t,d[N],cnt;bool g[N][N],vip[N];
int f[N][N],ans;
inline void read(int&a){for(a=0;*buf<48;buf++);while(*buf>47)a=a*10+*buf++-48;}
void toposort(int S){
  for(i=1;i<=n;i++)d[i]=0;
  for(i=1;i<=n;i++)if(vip[i]==S)for(j=1;j<=n;j++)if(vip[j]==S)d[j]+=g[i][j];
  for(h=i=1,t=cnt=0;i<=n;i++)if(vip[i]==S){
    cnt++;
    if(!d[i])q[++t]=i;
  }
  while(h<=t)for(x=q[h++],i=1;i<=n;i++)if(vip[i]==S&&g[x][i])if(!(--d[i]))q[++t]=i;
}
inline void up(int&a,int b){a<b?(a=b):0;}
inline int max(int a,int b){return a>b?a:b;}
int main(){
  fread(Buf,1,BUF,stdin);read(n),read(m);
  for(i=1;i<=n;i++)for(j=1;j<=n;j++)read(x),g[i][j]=x;
  for(i=1;i<=m;i++)read(x),vip[x]=1;
  toposort(1);
  if(t<cnt)return puts("impossible"),0;
  for(i=1;i<=cnt;i++)a[++ca]=q[i];
  toposort(0);
  for(i=1;i<=cnt;i++)b[++cb]=q[i];
  for(i=1;i<=ca;i++)d[a[i]]=i;
  for(i=1;i<=cb;i++){
    x=b[i];
    L=ca+1,R=0;
    for(j=1;j<=n;j++)if(vip[j]){
      y=d[j];
      if(g[x][j]){
        if(y<L)L=y;
      }else if(y>R)R=y;
    }
    for(j=0;j<=ca;j++)f[i][j]=f[i-1][j];
    if(L>R)for(j=0;j<L;j++)up(f[i][j>R?j:R],f[i-1][j]+1);
  }
  for(j=0;j<=ca;j++)up(ans,f[cb][j]);
  ans=cb-ans;
  if(ans>=m)puts("impossible");else printf("%d",ans);
  return 0;
}

  

posted @ 2019-02-16 00:33 Claris 阅读(...) 评论(...) 编辑 收藏