BZOJ2040 : [2009国家集训队]拯救Protoss的故乡

以根为原点,所有叶子为汇点建立网络。

对于一条边$(x,y,A,B)$,$x$向$y$连边,容量$A$,费用0,再连边,容量$B-A$,费用1。

然后不断增广,直到费用达到$M$为止的最大流即为答案。

考虑用树链剖分+线段树来模拟这个过程:

首先加入所有费用为0的边,每次求出到根距离最小的可增广的叶子,然后求出路径上容量的最小值,将所有边的容量减掉它。

如果减的过程中有边容量变为0,那么将它加入临时数组$q$中。

然后扫描$q$中所有边,如果一条边之前费用为0,那么把它容量设置为$B-A$,然后将子树内费用都加一,否则将它子树内的点都标记为不可增广。

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

 

#include<cstdio>
const int N=10010,M=33000,inf=~0U>>1;
int n,m,i,x,y,A,B,ans,cnt,q[N],mf[M],mc[M],tf[M],tc[M],td[M];
int g[N],v[N],nxt[N],f[N],d[N],wa[N],wb[N],size[N],son[N],top[N],st[N],en[N],dfn,seq[N],S[N];
void dfs(int x){
  size[x]=1;son[x]=-1;
  for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]){
    d[v[i]]=d[x]+1;dfs(v[i]);size[x]+=size[v[i]];
    if(son[x]<0||size[v[i]]>size[son[x]])son[x]=v[i];
  }
}
void dfs2(int x,int y){
  seq[st[x]=++dfn]=x;top[x]=y;
  if(~son[x])dfs2(son[x],y);
  for(int i=g[x];i;i=nxt[i])if(v[i]!=son[x]&&v[i]!=f[x])dfs2(v[i],v[i]);
  en[x]=dfn;
}
inline int min(int a,int b){return a<b?a:b;}
inline void tagf(int x,int y){mf[x]-=y;tf[x]+=y;}
inline void tagc(int x,int y){if(mc[x]<inf)mc[x]+=y;tc[x]+=y;}
inline void tagd(int x){mc[x]=inf;tc[x]=0;td[x]=1;}
inline void pb(int x){
  if(tf[x])tagf(x<<1,tf[x]),tagf(x<<1|1,tf[x]),tf[x]=0;
  if(tc[x])tagc(x<<1,tc[x]),tagc(x<<1|1,tc[x]),tc[x]=0;
  if(td[x])tagd(x<<1),tagd(x<<1|1),td[x]=0;
}
inline void up(int x){
  mf[x]=min(mf[x<<1],mf[x<<1|1]);
  mc[x]=min(mc[x<<1],mc[x<<1|1]);
}
void build(int x,int a,int b){
  if(a==b){
    int y=seq[a];
    mf[x]=wa[y];
    if(size[y]>1)mc[x]=inf;
    return;
  }
  int mid=(a+b)>>1;
  build(x<<1,a,mid),build(x<<1|1,mid+1,b),up(x);
}
void modifyf(int x,int a,int b,int c,int p){
  if(a==b){mf[x]=p;return;}
  pb(x);
  int mid=(a+b)>>1;
  if(c<=mid)modifyf(x<<1,a,mid,c,p);else modifyf(x<<1|1,mid+1,b,c,p);
  up(x);
}
void changef(int x,int a,int b,int c,int d,int p){
  if(c<=a&&b<=d&&mf[x]>p){tagf(x,p);return;}
  if(a==b){
    q[++cnt]=seq[a];
    mf[x]=0;
    return;
  }
  pb(x);
  int mid=(a+b)>>1;
  if(c<=mid)changef(x<<1,a,mid,c,d,p);
  if(d>mid)changef(x<<1|1,mid+1,b,c,d,p);
  up(x);
}
void changec(int x,int a,int b,int c,int d){
  if(c<=a&&b<=d){tagc(x,1);return;}
  pb(x);
  int mid=(a+b)>>1;
  if(c<=mid)changec(x<<1,a,mid,c,d);
  if(d>mid)changec(x<<1|1,mid+1,b,c,d);
  up(x);
}
void changed(int x,int a,int b,int c,int d){
  if(c<=a&&b<=d){tagd(x);return;}
  pb(x);
  int mid=(a+b)>>1;
  if(c<=mid)changed(x<<1,a,mid,c,d);
  if(d>mid)changed(x<<1|1,mid+1,b,c,d);
  up(x);
}
int askmf(int x,int a,int b,int c,int d){
  if(c<=a&&b<=d)return mf[x];
  pb(x);
  int mid=(a+b)>>1,t=inf;
  if(c<=mid)t=askmf(x<<1,a,mid,c,d);
  if(d>mid)t=min(t,askmf(x<<1|1,mid+1,b,c,d));
  return up(x),t;
}
inline int getmc(){
  int x=1,a=2,b=dfn,mid;
  while(a<b){
    mid=(a+b)>>1;
    if(mc[x<<1]<mc[x<<1|1])x<<=1,b=mid;else x=x<<1|1,a=mid+1;
  }
  return seq[a];
}
inline int getmf(int x){
  int t=inf;
  while(top[x])t=min(t,askmf(1,2,dfn,st[top[x]],st[x])),x=f[top[x]];
  if(st[x]>=2)t=min(t,askmf(1,2,dfn,2,st[x]));
  return t;
}
inline void chain(int x,int y){
  cnt=0;
  while(top[x])changef(1,2,dfn,st[top[x]],st[x],y),x=f[top[x]];
  if(st[x]>=2)changef(1,2,dfn,2,st[x],y);
  while(cnt){
    x=q[cnt--];
    if(!S[x]&&wb[x]>wa[x])modifyf(1,2,dfn,st[x],wb[x]-wa[x]),changec(1,2,dfn,st[x],en[x]);
    else changed(1,2,dfn,st[x],en[x]);
    S[x]++;
  }
}
int main(){
  scanf("%d%d",&n,&m);
  for(i=1;i<=n;i++){
    scanf("%d%d%d%d",&x,&y,&A,&B);
    v[i]=y,nxt[i]=g[x],g[x]=i;
    f[y]=x,wa[y]=A,wb[y]=B;
  }
  dfs(0),dfs2(0,0),build(1,2,dfn);
  while(mc[1]<inf){
    y=getmf(x=getmc());
    if(mc[1]*y>m)y=m/mc[1];
    if(!y)break;
    m-=mc[1]*y;
    ans+=y;
    chain(x,y);
  }
  return printf("%d",ans),0;
}

  

你可能感兴趣的:(BZOJ2040 : [2009国家集训队]拯救Protoss的故乡)