【树链剖分+线段树维护最小费用流】大灾变

48 篇文章 0 订阅
12 篇文章 0 订阅

恶心题

要写两个线段树还得是区间覆盖的,其中一个是树链剖分的,另一个是维护括号序列的。

有两种边,一种是免费的,一种是有代价的,每次取最短路,树形结构大约就不要退流了吧。

每次树链剖分找最短边,括号序列维护距离。

#include <cstdio>
#include <cstdlib>
#include <cstring>
const long long oo=1073741819;
const int maxn=200000,maxr=524288,maxl=500000;
struct sort {int o,d,i;}a[maxn];
int ans,n,m,tail[maxn],b[2][maxr],c[maxr],p[maxn],ss,next[maxl],sora[maxl],low[maxl],lim[maxl],m1,s,s1,l[maxn],r[maxn],high,st[maxn],rt[maxn],rs[maxn],f[maxn],o[maxn],ll[maxr],rr[maxr];
long long flow[maxn],t[2][maxr],dep[maxr],max[maxn],maxi[maxn],sum,d[2][maxr],lx[maxr],q[maxr],v[maxr];
void origin()
{
  int i;
  for (i=0;i<=n;i++) tail[i]=i;ss=n;
  for (m1=1,high=0;m1<=n+2;m1<<=1,high++) ;
}
void link(int x,int y,int z,int k)
{
  ss++,next[tail[x]]=ss,tail[x]=ss,sora[ss]=y,low[ss]=z,lim[ss]=k;
}
void dfs(int x)
{
  int i,ne;
  l[x]=oo,r[x]=-oo;
  if (next[x]==0) l[x]=r[x]=++s,b[0][s+m1]=x;
  for (i=x;next[i]!=0;) {
    i=next[i],ne=sora[i];
    dfs(ne);
    if (l[ne]<l[x]) l[x]=l[ne];
    if (r[ne]>r[x]) r[x]=r[ne];
  }
}
int minc(int e,int x,int y) {return (d[e][x]<d[e][y]) ? b[e][x] : b[e][y];}
int min(int e,int x,int y)  {return (d[e][x]<d[e][y]) ? d[e][x] : d[e][y];}
void pushdown(int e,int x)
{
  int xx,lson,rson;
  for (int i=high;i>=0;i--) {
    xx=x>>i;
    if (t[xx]) {
      lson=(xx<<1),rson=lson+1;
      d[e][lson]+=t[e][xx],d[e][rson]+=t[e][xx];
      t[e][lson]+=t[e][xx],t[e][rson]+=t[e][xx];
      t[e][xx]=0;
    }
  }
}
int ask(int e,int l,int r)
{
  int ll,rr,mini;
  l+=m1-1,r+=m1+1;
  ll=l>>1,rr=r>>1;
  pushdown(e,ll),pushdown(e,rr);
  for (sum=oo;!(1==(l^r));l>>=1,r>>=1) {
    if (0==(l&1)) if (d[e][l+1]<sum) sum=d[e][l+1],mini=b[e][l+1];
    if (1==(r&1)) if (d[e][r-1]<sum) sum=d[e][r-1],mini=b[e][r-1];
  }
  return mini;
}
void updata(int e,int x) {for (;x;x>>=1) b[e][x]=minc(e,x<<1,(x<<1)+1),d[e][x]=min(e,x<<1,(x<<1)+1);}
void change(int e,int l,int r,int k)
{
  l+=m1-1,r+=m1+1;
  int ll=l>>1,rr=r>>1;
  pushdown(e,ll),pushdown(e,rr);
  for (;!(1==(l^r));l>>=1,r>>=1) {
    if (0==(l&1)) d[e][l+1]+=k,t[e][l+1]+=k;
    if (1==(r&1)) d[e][r-1]+=k,t[e][r-1]+=k;
  }
  updata(e,ll),updata(e,rr);
}
void bfs(int s)
{
  int h,r,ne,i,na;
  h=r=0,st[r=1]=s,dep[s]=0;
  for (;h<r;) {
    ne=st[++h];
    for (i=ne;next[i]!=0;) {
      i=next[i],na=sora[i];
      dep[na]=dep[ne]+1,rt[na]=ne,rs[na]=i,st[++r]=na;
    }
  }
  memset(max,129,sizeof(max));
  for (i=r;i>=1;i--) {
    ne=st[i],na=rt[ne];
    f[na]+=++f[ne];
    if (f[ne]>max[na]) max[na]=f[ne],maxi[na]=ne;
    if (maxi[ne]!=0) {
      na=maxi[ne];
      if (o[na]==0) o[na]=++s1;
      o[ne]=o[na];
    }
  }
}
int cmp(const void *i,const void *j)
{
  sort p=*(sort *)i,q=*(sort *)j;
  if (p.o!=q.o) return p.o-q.o;
  return p.d-q.d;
}
void ori()
{
  int i,j;
  for (i=1;i<=n;i++) a[i].o=o[i],a[i].d=dep[i],a[i].i=i;
  qsort(a+1,n,sizeof(a[1]),cmp);
  for (i=1;(0==a[i].o);i++) ;j=i;
  for (s1=0;i<=n;i++) {
    ++s1;
    if (a[i].o!=a[i-1].o) ll[a[i].o]=s1,lx[a[i].o]=a[i].i,rr[a[i-1].o]=s1-1;
    p[a[i].i]=s1;b[1][s1+m1]=a[i].i;q[a[i].i]=i;
  }
  rr[a[n].o]=s1;
  change(1,p[1],p[1],oo+oo);
  for (i=j;i<=n;i++) if (a[i].i!=1) change(1,p[a[i].i],p[a[i].i],low[rs[a[i].i]]-oo);
  for (i=1;i<j;i++) flow[a[i].i]=low[rs[a[i].i]];
}
int search(int x,int &z)
{
  int min=oo,mini=0,cos;
  for (;x!=1;) {
    if (p[x]) {
      cos=ask(1,ll[a[q[x]].o],p[x]);
      if (sum<min) min=sum,mini=cos;
      x=rt[lx[a[q[x]].o]];
    }
    else {
      if (flow[x]<min) min=flow[x],mini=x;
      x=rt[x];
    }
  }
  z=min;
  return mini;
}
void sign(int x,int k)
{
  for (;x!=1;) {
    if (p[x]) {
      change(1,ll[a[q[x]].o],p[x],k),x=rt[lx[a[q[x]].o]];
    } 
    else {
      flow[x]+=k;
      x=rt[x];
    }
  }
}
void init()
{
  int i,x,y,z,k,sum1;
  scanf("%d%d\n",&n,&m);n++;
  origin();
  for (i=1;i<=n-1;i++) scanf("%d%d%d%d",&x,&y,&z,&k),link(x+1,y+1,z,k);
  s1=0,dfs(1),bfs(1);
  for (i=1;i<=m1+m1;i++) d[0][i]=d[1][i]=oo;  
  ori();
  for (i=1;i<=n;i++) if (b[0][l[i]+m1]==i) change(0,l[i],l[i],-oo);
  ans=sum1=z=0,rt[1]=1;
  for (;;) {
//    printf("%d %d\n",ans,sum1);
    x=ask(0,1,n);sum1=sum;
    if (sum1>=oo) break;
    y=search(x,z);
    if (sum1*z<=m) m-=sum1*z,ans+=z;
    else {ans+=m/sum1,m=0;break;}
    if (v[y]) change(0,l[y],r[y],oo),sign(x,-z);
    else {
      v[y]=1;
      change(0,l[y],r[y],1);
      if (z) sign(x,-z);
      if (p[y]) {change(1,p[y],p[y],lim[rs[y]]-low[rs[y]]);}
      else flow[y]=lim[rs[y]]-z;
    }
  }
  printf("%d\n",ans);
}
int main()
{
  freopen("input.txt","r",stdin);
  freopen("output.txt","w",stdout);
    init();
  return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值