【BZOJ 4155】Humble Captains

  • 第一问水水的最小割,从来没有写得如此愉快。
    假设两个集合A,B
    第二问等价于min($| \sigma_i [i in A] D[i] - \sigma_i [i in B] D[i] |$),01背包就好了

  • #include<cstdio>


    #include<bitset>


    using namespace std;


    const int N=40010,inf=~0U>>2;


    struct edge{int t,f;edge*nxt,*pair;}*g[N],*d[N],pool[240000],*cur=pool;


    int Case,n,m,cnt,i,x,y,S,T,h[N],gap[N],maxflow,du[N],ans;bitset<19910>f;


    inline int abs(int x){return x>0?x:-x;}


    inline int min(int x,int y){return x<y?x:y;}


    inline void add(int s,int t,int f){


      edge*p=cur++;p->t=t;p->f=f;p->nxt=g[s];g[s]=p;


      p=cur++;p->t=s;p->f=0;p->nxt=g[t];g[t]=p;


      g[s]->pair=g[t];g[t]->pair=g[s];


    }


    int sap(int v,int flow){


      if(v==T)return flow;


      int rec=0;


      for(edge*p=d[v];p;p=p->nxt)if(h[v]==h[p->t]+1&&p->f){


        int ret=sap(p->t,min(flow-rec,p->f));


        p->f-=ret;p->pair->f+=ret;d[v]=p;


        if((rec+=ret)==flow)return flow;


      }


      if(!(--gap[h[v]]))h[S]=T;


      gap[++h[v]]++;d[v]=g[v];


      return rec;


    }


    int main(){


      for(scanf("%d",&Case);Case--;printf("%d\n",ans)){


        scanf("%d%d",&n,&m),S=n+m+m+1,T=S+1,cnt=n;


        for(i=1;i<=m;i++){


          scanf("%d%d",&x,&y);


          du[x]++,du[y]++;


          add(S,++cnt,1),add(++cnt,T,1);


          add(cnt-1,x,inf),add(x,cnt,inf);


          add(cnt-1,y,inf),add(y,cnt,inf);


        }


        add(S,1,inf),add(2,T,inf);


        for(gap[maxflow=0]=T,i=1;i<=T;i++)d[i]=g[i];


        while(h[S]<T)maxflow+=sap(S,inf);


        printf("%d ",m*2-maxflow);


        for(cur=pool,i=0;i<=T;i++)g[i]=d[i]=NULL,h[i]=gap[i]=0;


        for(f.reset(),f[0]=1,i=3;i<=n;i++)f|=f<<du[i];


        for(ans=inf,i=0;i<=m;i++)if(f[i])ans=min(ans,abs(du[1]+i-m));


        for(i=1;i<=n;i++)du[i]=0;


      }


      return 0;


    }




评论(1)

© yjq_1999 | Powered by LOFTER