题目描述

在星历2012年,星灵英雄Zeratul预测到他所在的Aiur行星在M天后会发生持续性暴雨灾害,尤其是他们的首都。而Zeratul作为星灵族的英雄,当然是要尽自己最大的努力帮助星灵族渡过这场自然灾害。要渡过这场自然灾害,Zeratul自然要安排很多很多事情,其中一件就是将雨水疏导到大海里去。星灵族在重建家园的时候建造了N条河流,这些河流连接了共N+1个城市,当然其中包括了星灵首都。城市的编号为0…N,星灵首都的编号为0。当然水流是有方向的,除了星灵首都以外,其余的城市都有且仅有一条河流流入。如果一个城市没有流出去的河流,那么这个城市就是一个沿海城市,可以认为流入这个城市的河流是直接流入大海的。聪明的星灵族在建造河流的时候是不会让其出现环状结构的,也就是说所有的河流都是能够流入大海的。每一条河流都是有一个最大流量的,一旦超过这个最大流量,就会发生洪水灾害。因此从星灵首都流入大海的总水流量是有一个最大值的。例如有3条河流:第一条是从城市0流向城市1,最大流量为4;第二条是从城市1流向城市2,最大流量为2;第三条是从城市1流向城市3,最大流量为3。此时从星灵首都(城市0)流入大海的总水流量最大为4,方案有两种: A. 第一条河流的流量为4,第二条河流的流量为2,第三条河流的流量为2。 B. 第一条河流的流量为4,第二条河流的流量为1,第三条河流的流量为3。由于离暴雨到来还有时间,因此Zeratul可以扩大某些河流的最大流量来使得从星灵首都流入大海的总水流量增大。比如将上面这个例子的第一条河流的最大流量增大1,那么从星灵首都流入大海的总流水量也可以增大1,变成了5。当然将河流的最大流量扩大是需要时间的,将一条河流的最大流量扩大1所需要的时间为1天。离暴雨到来还有M天,因此Zeratul也有M天的时间来扩大河流的最大流量。不过由于河流周围的地形限制,每条河流并不是能够无限扩大的,因此每条河流的可以扩大到的最大流量也是有限制的。而此时Zeratul正忙着安排别的工作,因此他将这个艰巨的任务交给了你。你需要安排一种方案,使得从星灵首都流入大海的总水流量最大。不过现在你只需要告诉Zeratul这个最大值即可。

输入

第一行包含一个正整数N和一个非负整数M。其中N表示河流的个数,M表示离暴雨到来还剩下的天数。接下来N行,每行四个正整数U、V、A、B。其中U和V为该河流所连接的两个城市,且河流的水流方向为从城市U流向城市V,A和B表示该河流当前的最大流量和可以扩大到的最大流量的上限。输入数据保证合法。

输出

仅包含一个整数,表示从星灵首都流入大海的最大总水流量。

样例输入

5 7
0 1 4 8
0 4 1 6
1 2 2 10
1 3 3 5
4 5 6 6

样例输出

11

提示

将第一条河流的最大流量扩大1,变为5。将第二条河流的最大流量扩大5,变为6。第三条河流的最大流量不扩大,仍然为2。第四条河流的最大流量不扩大,仍然为3。第五条河流的最大流量不扩大,仍然为6。此时从星灵首都流入大海的总水流量为6+3+2=11。 1≤A≤B≤100000 N< = 10000 M< = 1000000

第一段为部分分做法及满分做法的铺垫,可以直接看第二段QvQ。

对于部分分数据我们可以用树形$DP$做,即$f[i][j]$表示$i$节点子树中耗费$j$时间能得到的最大流量,枚举$i$的子节点$to$可以得到方程$f[i][j]=max(f[i][j],f[i][j-k-l]+min(a[to]+l,f[to][k],b[to]))$($a[to],b[to]$表示$i$到$to$的两种边权)。这样的时间复杂度是$O(nm^3)$。但可以发现$min(a[to]+l,f[to][k],b[to])$与$i$无关,我们可以用$g[to][j+k]$来记录$min(a[to]+l,f[to][k],b[to])$的最大值,这样得到转移方程$f[i][j]=max(f[i][j],f[i][j-k]+g[to][k]),g[to][k]=max(min(a[to]+l,b[to],f[to][k-l])$,时间复杂度降到了$O(nm^2)$。但如果我们不关注这是树的问题可以发现这实际上就是一个最小费用最大流,将每条边拆成两条边,一条边容量为$a$,费用为$0$;另一条边容量为$b-a$,费用为$1$。这样时间复杂度降到了$O(n^2)$。

这时我们再关注它是一棵树时就可以利用树的一些特性来对费用流进行优化:可以发现到每个叶子结点(即汇点)的路径只有一条,而根据费用流原理:每次选择一条费用最小的路径并将其跑到满流。我们可以模拟这个过程:记录根节点到每个叶子结点的费用的最小值(假设这个点为$x$),那么这一次增广的路径就是根到$x$的路径,流量就是路径上的容量最小值。我们将每条边的初始容量设为$a$,如果一次增广中容量最小值的边为$(u,v)$,容量为$f$,那么整条路径上所有边的容量都要减少$f$。如果当前这条边费用为$0$,我们将这条边容量变为$b-a$,这条边下端点子树中的所有叶子结点的费用就要$+1$;如果当前这条边费用为$1$,那么说明这条边下端点子树中所有叶子结点都不能再增广了,我们将子树中所有叶子结点的费用设为$INF$。在每次增广时可能存在有多条边同时满流的情况,我们只需要对一条边进行修改即可,其他边在下几次增广时就会相继被修改。

那么我们需要完成的操作有:

1、区间修改一段连续叶子结点的权值

2、查询所有叶子结点的权值最小值

3、查询一个叶子结点到根路径上的容量最小值

4、修改一条边的容量

5、将一个叶子节点到根路径上的容量减少

我们将叶子结点按$dfs$序排序,那么一个点子树中的叶子结点一定是连续的一段,前两个操作使用线段树即可完成;后三个操作使用LCT或树链剖分+线段树就可解决。

因为每一次增广都会让一条边满流,而每条边最多满流$2$次,所以时间复杂度为$O(nlog_{n}^2)$。

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<bitset>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define pr pair<int,int>
using namespace std;
int L[10010];
int R[10010];
pr mx[80010];
int add[80010];
int son[10010];
int top[10010];
int dep[10010];
int s[10010];
int a[10010];
int b[10010];
int q[10010];
int size[10010];
int f[10010];
int va[10010];
int vb[10010];
int head[10010];
int to[10010];
int next[10010];
pr mn[80010];
int red[80010];
int sig[80010];
int t[10010];
int cnt;
int u,v;
int tot;
int x,y;
int n,m;
int dfn;
int flow;
inline void add_edge(int u,int v,int x,int y)
{
next[++tot]=head[u];
head[u]=tot;
to[tot]=v;
va[tot]=x;
vb[tot]=y;
}
inline void dfs(int x)
{
size[x]=1;
L[x]=cnt+1;
for(int i=head[x];i;i=next[i])
{
f[to[i]]=x;
dep[to[i]]=dep[x]+1;
a[to[i]]=va[i];
b[to[i]]=vb[i];
dfs(to[i]);
size[x]+=size[to[i]];
if(size[to[i]]>size[son[x]])
{
son[x]=to[i];
}
}
if(!head[x])
{
cnt++;
t[cnt]=x;
}
R[x]=cnt;
}
inline void dfs2(int x,int tp)
{
top[x]=tp;
s[x]=++dfn;
q[dfn]=x;
if(son[x])
{
dfs2(son[x],tp);
}
for(int i=head[x];i;i=next[i])
{
if(to[i]!=son[x])
{
dfs2(to[i],to[i]);
}
}
}
inline pr cmp(pr x,pr y)
{
return x.first<y.first?x:y;
}
inline void updata(int rt)
{
mx[rt]=cmp(mx[rt<<1],mx[rt<<1|1]);
}
inline void downdata(int rt)
{
if(add[rt])
{
add[rt<<1]+=add[rt];
add[rt<<1|1]+=add[rt];
mx[rt<<1].first+=add[rt];
mx[rt<<1|1].first+=add[rt];
add[rt]=0;
}
}
inline void build2(int rt,int l,int r)
{
if(l==r)
{
mx[rt]=make_pair(0,t[l]);
return ;
}
int mid=(l+r)>>1;
build2(rt<<1,l,mid);
build2(rt<<1|1,mid+1,r);
updata(rt);
}
inline void increase(int rt,int l,int r,int L,int R,int k)
{
if(L<=l&&r<=R)
{
add[rt]+=k;
mx[rt].first+=k;
return ;
}
downdata(rt);
int mid=(l+r)>>1;
if(L<=mid)
{
increase(rt<<1,l,mid,L,R,k);
}
if(R>mid)
{
increase(rt<<1|1,mid+1,r,L,R,k);
}
updata(rt);
}
inline void pushup(int rt)
{
mn[rt]=cmp(mn[rt<<1],mn[rt<<1|1]);
}
inline void pushdown(int rt)
{
if(red[rt])
{
red[rt<<1]+=red[rt];
red[rt<<1|1]+=red[rt];
mn[rt<<1].first-=red[rt];
mn[rt<<1|1].first-=red[rt];
red[rt]=0;
}
}
inline void build(int rt,int l,int r)
{
if(l==r)
{
mn[rt]=make_pair(a[q[l]],q[l]);
return ;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
pushup(rt);
}
inline pr query_min(int rt,int l,int r,int L,int R)
{
if(L<=l&&r<=R)
{
return mn[rt];
}
pushdown(rt);
int mid=(l+r)>>1;
if(L>mid)
{
return query_min(rt<<1|1,mid+1,r,L,R);
}
else if(R<=mid)
{
return query_min(rt<<1,l,mid,L,R);
}
else
{
return cmp(query_min(rt<<1,l,mid,L,R),query_min(rt<<1|1,mid+1,r,L,R));
}
}
inline void reduce(int rt,int l,int r,int L,int R,int k)
{
if(L<=l&&r<=R)
{
red[rt]+=k;
mn[rt].first-=k;
return ;
}
pushdown(rt);
int mid=(l+r)>>1;
if(L<=mid)
{
reduce(rt<<1,l,mid,L,R,k);
}
if(R>mid)
{
reduce(rt<<1|1,mid+1,r,L,R,k);
}
pushup(rt);
}
inline void change(int rt,int l,int r,int k)
{
if(l==r)
{
if(!sig[rt])
{
sig[rt]=1;
mn[rt]=make_pair(b[q[l]]-a[q[l]],q[l]);
increase(1,1,cnt,L[q[l]],R[q[l]],1);
return ;
}
else
{
mn[rt]=make_pair(0,q[l]);
increase(1,1,cnt,L[q[l]],R[q[l]],10010);
return ;
}
}
pushdown(rt);
int mid=(l+r)>>1;
if(k<=mid)
{
change(rt<<1,l,mid,k);
}
else
{
change(rt<<1|1,mid+1,r,k);
}
pushup(rt);
}
inline pr query(int x)
{
pr res=make_pair(1000000000,0);
while(top[x]!=n+1)
{
res=cmp(res,query_min(1,1,n+1,s[top[x]],s[x]));
x=f[top[x]];
}
if(s[top[x]]+1>s[x])
{
return res;
}
res=cmp(res,query_min(1,1,n+1,s[top[x]]+1,s[x]));
return res;
}
inline void reduce_flow(int x,int k)
{
while(top[x]!=n+1)
{
reduce(1,1,n+1,s[top[x]],s[x],k);
x=f[top[x]];
}
if(s[top[x]]+1>s[x])
{
return ;
}
reduce(1,1,n+1,s[top[x]]+1,s[x],k);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d%d",&u,&v,&x,&y);
if(!u)
{
u=n+1;
}
add_edge(u,v,x,y);
}
dfs(n+1);
dfs2(n+1,n+1);
build(1,1,n+1);
build2(1,1,cnt);
while(1)
{
pr ans=mx[1];
pr res=query(ans.second);
if(ans.first>n)
{
break;
}
if(m>=res.first*ans.first)
{
m-=res.first*ans.first;
flow+=res.first;
reduce_flow(ans.second,res.first);
change(1,1,n+1,s[res.second]);
}
else
{
int sum=m/ans.first;
m-=sum*ans.first;
flow+=sum;
break;
}
}
printf("%d",flow);
}

BZOJ2040[2009国家集训队]拯救Protoss的故乡——模拟费用流+线段树+树链剖分的更多相关文章

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

    以根为原点,所有叶子为汇点建立网络. 对于一条边$(x,y,A,B)$,$x$向$y$连边,容量$A$,费用0,再连边,容量$B-A$,费用1. 然后不断增广,直到费用达到$M$为止的最大流即为答案. ...

  2. BZOJ 2039: [2009国家集训队]employ人员雇佣

    2039: [2009国家集训队]employ人员雇佣 Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 1369  Solved: 667[Submit ...

  3. BZOJ 2038: [2009国家集训队]小Z的袜子(hose) [莫队算法]【学习笔记】

    2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 7687  Solved: 3516[Subm ...

  4. BZOJ 2038: [2009国家集训队]小Z的袜子(hose)

    2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 7676  Solved: 3509[Subm ...

  5. 莫队算法 2038: [2009国家集训队]小Z的袜子(hose)

    链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2038 2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 ...

  6. BZOJ 2038 [2009国家集训队]小Z的袜子 莫队

    2038: [2009国家集训队]小Z的袜子(hose) 题目连接: http://www.lydsy.com/JudgeOnline/problem.php?id=2038 Descriptionw ...

  7. Bzoj 2038: [2009国家集训队]小Z的袜子(hose) 莫队,分块,暴力

    2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 5763  Solved: 2660[Subm ...

  8. BZOJ2038: [2009国家集训队]小Z的袜子(hose) -- 莫队算法 ,,分块

    2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 3577  Solved: 1652[Subm ...

  9. BZOJ 2038: [2009国家集训队]小Z的袜子(hose) ( 莫队 )

    莫队..先按sqrt(n)分块, 然后按块的顺序对询问排序, 同块就按右端点排序. 然后就按排序后的顺序暴力求解即可. 时间复杂度O(n1.5) --------------------------- ...

随机推荐

  1. 朱晔的互联网架构实践心得S1E3:相辅相成的存储五件套

    朱晔的互联网架构实践心得S1E3:相辅相成的存储五件套 [下载本文PDF进行阅读] 这里所说的五件套是指关系型数据库.索引型数据库.时序型数据库.文档型数据库和缓存型数据库. 上图显示了一套读写服务搭 ...

  2. 将 ASP.NET Core 2.0 项目升级至 ASP.NET Core 2.1.3X

    在上一篇文章ASP.Net Core 运行错误 Http Error 502.5 解决办法的最后有提到说,最推荐的升级办法是从2.0升级到2.1X版本. 操作如下 项目的例子直接使用https://g ...

  3. 微软Azure AspNetCore微服务实战第1期【补充2017-09-09活动】

    2017年09月09日,冒着酷暑,我们在(上海徐汇)虹桥路3号港汇中心2座10层组织了一次微软Azure AspNetCore微服务实战活动. 由于前期工作繁忙,活动完成之后,没能及时发布相关信息,特 ...

  4. 通过 Systemd Journal 收集日志

    随着 systemd 成了主流的 init 系统,systemd 的功能也在不断的增加,比如对系统日志的管理.Systemd 设计的日志系统好处多多,这里笔者就不再赘述了,本文笔者主要介绍 syste ...

  5. mysql创建数据库命令

    CREATE DATABASE IF NOT EXISTS yourdbname DEFAULT CHARSET utf8 COLLATE utf8_general_ci;

  6. 【kindle笔记】之 《鬼吹灯》-9-20

    [kindle笔记]读书记录-总 9-20 日常吐槽 连着几天,基本是一口气读完了鬼吹灯. 想来,也算是阴差阳错了.本来是想看盗墓的,读了几页开头,心想坏了,拷贝错了,这是鬼吹灯-- 讲真的,每每读小 ...

  7. Mysql连接数、线程数、数据包

    https://blog.csdn.net/qq_26545305/article/details/79675507

  8. centos6.7用yum安装redis解决办法及IP限制配置

    在centos6.7用yum安装redis解决办法 - bluesky1 - 博客园 http://www.cnblogs.com/lanblogs/p/6104834.html yum instal ...

  9. [转帖]xserver相关知识汇总

    xserver相关知识汇总 https://blog.csdn.net/QTVLC/article/details/81739984   本文主要是从以下几个方面介绍xorg-xserver 相关的知 ...

  10. vs2012密钥

    Microsoft Visual Studio Ultimate 2012 旗舰版 有效注册密钥:YKCW6-BPFPF-BT8C9-7DCTH-QXGWC:KCW6-BPFPF-BT8C9-7DCT ...