【BZOJ4712】洪水

Description

小A走到一个山脚下,准备给自己造一个小屋。这时候,小A的朋友(op,又叫管理员)打开了创造模式,然后飞到山顶放了格水。于是小A面前出现了一个瀑布。作为平民的小A只好老实巴交地爬山堵水。那么问题来了:我们把这个瀑布看成是一个n个节点的树,每个节点有权值(爬上去的代价)。小A要选择一些节点,以其权值和作为代价将这些点删除(堵上),使得根节点与所有叶子结点不连通。问最小代价。不过到这还没结束。小A的朋友觉得这样子太便宜小A了,于是他还会不断地修改地形,使得某个节点的权值发生变化。不过到这还没结束。小A觉得朋友做得太绝了,于是放弃了分离所有叶子节点的方案。取而代之的是,每次他只要在某个子树中(和子树之外的点完全无关)。于是他找到你。

Input

输入文件第一行包含一个数n,表示树的大小。

接下来一行包含n个数,表示第i个点的权值。
接下来n-1行每行包含两个数fr,to。表示书中有一条边(fr,to)。
接下来一行一个整数,表示操作的个数。
接下来m行每行表示一个操作,若该行第一个数为Q,则表示询问操作,后面跟一个参数x,表示对应子树的根;若为C,则表示修改操作,后面接两个参数x,to,表示将点x的权值加上to。
n<=200000,保证任意to都为非负数

Output

对于每次询问操作,输出对应的答案,答案之间用换行隔开。

Sample Input

4
4 3 2 1
1 2
1 3
4 2
4
Q 1
Q 2
C 4 10
Q 1

Sample Output

3
1
4

题解:设每个点的DP值是f[x],每个点儿子的f之和为g[x],f[x]=min(g[x],v[x])。发现,由于v[x]只能变大,所以每次操作可能导致一些点从选g[x]变成选v[x],但只能导致一个点从选v[x]变成选g[x],所以对于从选v变成选g或从选g变成选v的,我们都可以暴力搞定(均摊的思想)。具体做法:

先改v[x],如果v[x]的增长导致了f[x]的改变,则令它父亲的g值+=改变值,然后递归它的父亲:

先让当前的g值+=该变量,如果f不变,则结束;如果原来选g,改完变成选v,那么记录该变量,继续修改它的父亲;如果原来选g,改完还是选g,那么一定是:当前点的连续几辈祖先都是原来选g改完也选g,那么我们二分找到深度最小的祖先,然后中间的g值都能用线段树直接修改。然后再继续修改这个祖先的父亲即可。

那么二分的过程具体如何实现呢?需要用线段树维护v[x]-g[x]的最小值,然后再树剖的时候,先判断当前链的链顶是否符合要求,如果不是,则在链内二分,否则在直接跳到下一条链上。

这样就保证了时间复杂度是$O(nlog_n^2)$的。

#include <cstdio>
#include <cstring>
#include <iostream>
#define lson x<<1
#define rson x<<1|1
using namespace std;
const int maxn=200010;
typedef long long ll;
int n,m,cnt;
int to[maxn<<1],next[maxn<<1],head[maxn],p[maxn],Q[maxn],dep[maxn],fa[maxn],top[maxn],siz[maxn],son[maxn];
char str[5];
ll sv[maxn<<2],ts[maxn<<2],sc[maxn<<2],v[maxn],g[maxn];
inline void add(int a,int b)
{
to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
}
void dfs1(int x)
{
siz[x]=1;
for(int i=head[x];i!=-1;i=next[i]) if(to[i]!=fa[x])
{
fa[to[i]]=x,dep[to[i]]=dep[x]+1,dfs1(to[i]),siz[x]+=siz[to[i]];
if(siz[to[i]]>siz[son[x]]) son[x]=to[i];
g[x]+=min(g[to[i]],v[to[i]]);
}
if(siz[x]==1) g[x]=1<<30;
}
void dfs2(int x,int tp)
{
p[x]=++p[0],Q[p[0]]=x,top[x]=tp;
if(son[x]) dfs2(son[x],tp);
for(int i=head[x];i!=-1;i=next[i]) if(to[i]!=fa[x]&&to[i]!=son[x]) dfs2(to[i],to[i]);
}
inline void pushdown(int x)
{
if(ts[x]) sc[lson]-=ts[x],sc[rson]-=ts[x],ts[lson]+=ts[x],ts[rson]+=ts[x],ts[x]=0;
}
void build(int l,int r,int x)
{
if(l==r)
{
sv[x]=v[Q[l]],sc[x]=v[Q[l]]-g[Q[l]];
return ;
}
int mid=(l+r)>>1;
build(l,mid,lson),build(mid+1,r,rson);
sv[x]=min(sv[lson],sv[rson]),sc[x]=min(sc[lson],sc[rson]);
}
ll getg(int l,int r,int x,int a)
{
if(l==r) return sv[x]-sc[x];
pushdown(x);
int mid=(l+r)>>1;
if(a<=mid) return getg(l,mid,lson,a);
return getg(mid+1,r,rson,a);
}
ll getv(int l,int r,int x,int a,int b)
{
if(l==r) return sv[x];
pushdown(x);
int mid=(l+r)>>1;
if(b<=mid) return getv(l,mid,lson,a,b);
if(a>mid) return getv(mid+1,r,rson,a,b);
return min(getv(l,mid,lson,a,b),getv(mid+1,r,rson,a,b));
}
ll getc(int l,int r,int x,int a,int b)
{
if(a<=l&&r<=b) return sc[x];
pushdown(x);
int mid=(l+r)>>1;
if(b<=mid) return getc(l,mid,lson,a,b);
if(a>mid) return getc(mid+1,r,rson,a,b);
return min(getc(l,mid,lson,a,b),getc(mid+1,r,rson,a,b));
}
void upg(int l,int r,int x,int a,int b,ll c)
{
if(a<=l&&r<=b)
{
if(c==-1) sc[x]+=v[Q[l]]-sv[x],sv[x]=v[Q[l]];
else sc[x]-=c,ts[x]+=c;
return ;
}
pushdown(x);
int mid=(l+r)>>1;
if(a<=mid) upg(l,mid,lson,a,b,c);
if(b>mid) upg(mid+1,r,rson,a,b,c);
sv[x]=min(sv[lson],sv[rson]),sc[x]=min(sc[lson],sc[rson]);
}
inline void change(int x,ll d)
{
while(x)
{
ll gx=getg(1,n,1,p[x]),fx=min(gx,v[x]);
upg(1,n,1,p[x],p[x],d);
if(gx>=v[x]) return ;
if(gx+d>v[x]) d=v[x]-fx,x=fa[x];
else
{
if(top[x]==x) x=fa[x];
else if(getc(1,n,1,p[top[x]],p[x]-1)>=d)
{
upg(1,n,1,p[top[x]],p[x]-1,d);
x=fa[top[x]];
}
else
{
int l=p[top[x]],r=p[x],mid;
while(l<r)
{
mid=(l+r)>>1;
if(getc(1,n,1,mid,p[x]-1)>=d) r=mid;
else l=mid+1;
}
if(r<p[x]) upg(1,n,1,r,p[x]-1,d);
x=Q[r-1];
}
}
}
}
inline int rd()
{
int ret=0,f=1; char gc=getchar();
while(gc<'0'||gc>'9') {if(gc=='-') f=-f; gc=getchar();}
while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar();
return ret*f;
}
int main()
{
n=rd();
int i,a,b;
for(i=1;i<=n;i++) v[i]=rd();
memset(head,-1,sizeof(head));
for(i=1;i<n;i++) a=rd(),b=rd(),add(a,b),add(b,a);
dep[1]=1,dfs1(1),dfs2(1,1);
m=rd();
build(1,n,1);
for(i=1;i<=m;i++)
{
scanf("%s",str);
if(str[0]=='C')
{
a=rd(),b=rd();
ll gx=getg(1,n,1,p[a]),fx=min(gx,v[a]);
v[a]+=b,upg(1,n,1,p[a],p[a],-1);
if(min(v[a],gx)>fx) change(fa[a],min(v[a],gx)-fx);
}
else a=rd(),printf("%lld\n",min(v[a],getg(1,n,1,p[a])));
}
return 0;
}//4 4 3 2 1 1 2 1 3 4 2 4 Q 1 Q 2 C 4 10 Q 1

【BZOJ4712】洪水 树链剖分优化DP+线段树的更多相关文章

  1. BZOJ4012[HNOI2015]开店——树链剖分+可持久化线段树/动态点分治+vector

    题目描述 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到 人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的 想法当然非常好啦,但是她们也发现她们面临着一个 ...

  2. BZOJ 2243: [SDOI2011]染色 树链剖分 倍增lca 线段树

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/pr ...

  3. BZOJ 2243:染色(树链剖分+区间合并线段树)

    [SDOI2011]染色Description给定一棵有n个节点的无根树和m个操作,操作有2类:1.将节点a到节点b路径上所有点都染成颜色c:2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认 ...

  4. 【Codeforces】【网络流】【树链剖分】【线段树】ALT (CodeForces - 786E)

    题意 现在有m个人,每一个人都特别喜欢狗.另外还有一棵n个节点的树. 现在每个人都想要从树上的某个节点走到另外一个节点,且满足要么这个人自带一条狗m,要么他经过的所有边h上都有一条狗. 2<=n ...

  5. LOJ2269 [SDOI2017] 切树游戏 【FWT】【动态DP】【树链剖分】【线段树】

    题目分析: 好题.本来是一道好的非套路题,但是不凑巧的是当年有一位国家集训队员正好介绍了这个算法. 首先考虑静态的情况.这个的DP方程非常容易写出来. 接着可以注意到对于异或结果的计数可以看成一个FW ...

  6. Codeforces 856D - Masha and Cactus(树链剖分优化 dp)

    题面传送门 题意: 给你一棵 \(n\) 个顶点的树和 \(m\) 条带权值的附加边 你要选择一些附加边加入原树中使其成为一个仙人掌(每个点最多属于 \(1\) 个简单环) 求你选择的附加边权值之和的 ...

  7. 【BZOJ3681】Arietta 树链剖分+可持久化线段树优化建图+网络流

    [BZOJ3681]Arietta Description Arietta 的命运与她的妹妹不同,在她的妹妹已经走进学院的时候,她仍然留在山村中.但是她从未停止过和恋人 Velding 的书信往来.一 ...

  8. HDU 5029 Relief grain 树链剖分打标记 线段树区间最大值

    Relief grain Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid= ...

  9. HYSBZ 4034 【树链剖分】+【线段树 】

    <题目链接> 题目大意: 有一棵点数为 N 的树,以点 1 为根,且树点有权值.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x ...

随机推荐

  1. 【前端】jQuery

    一.jQuery是什么? 1.jQuery由美国人John Resig创建,至今已吸引了来自世界各地的众多JavaScript高手加入其team 2.jQuery是继prototype之后有一个优秀的 ...

  2. Java包装类的常量池

     Integer a=Integer.valueOf(100); Integer b=Integer.valueOf(100); System.out.println(a==b); Double d1 ...

  3. easyui treegrid增、删、改及批量保存

    treegrid添加新行和删除行的方法: 添加和删除直接调用这两个方法即可,修改的方法有点特殊,treegrid没有提供update方法,设置行为编辑状态,$('#obj_tgd_objectlist ...

  4. 采集音频和摄像头视频并实时H264编码及AAC编码

    转自:http://www.cnblogs.com/haibindev/archive/2011/11/10/2244442.html 0. 前言 我在前两篇文章中写了DirectShow捕获音视频然 ...

  5. python打包exe文件-ImportError: No module named 'queue'

    我之前遇到的一个错误就是 File "site-packages\urllib3\packages\six.py", line 92, in __get__ File " ...

  6. 【转】Cocos2d-x 3.1.1 学习日志6--30分钟了解C++11新特性

    [转]Cocos2d-x 3.1.1 学习日志6--30分钟了解C++11新特性 Cocos2d-x 3.1.1 学习日志6--30分钟了解C++11新特性

  7. Win7下 OpenCV+Qt开发环境搭建

    1.所需软件工具: (1)OpenCV开发库,2.4.9版:包括源文件(source文件夹)和编译后的文件(build文件夹),但最好自己使用CMake又一次编译.否则easy出错. (2)Qt Cr ...

  8. Unix系统编程(四)creat系统调用

    我好疑惑啊,creat系统调用为啥没有以e结尾呢?搞得我每次都怀疑我敲错了. 在早期的UNIX实现中,open只有两个参数,无法创建新文件,而是使用creat系统调用创建并打开一个新文件. int c ...

  9. JVM Specification 9th Edition (4) Chapter 3. Compiling for the Java Virtual Machine

    Chapter 3. Compiling for the Java Virtual Machine 内容列表 3.1. Format of Examples 3.2. Use of Constants ...

  10. Nginx中修改php.ini的上传设置upload_max_filesize的值

    普遍的网络越来越快,以前小家子气的2M上传限制慢慢变得不合时宜了.最近就把2M的限制直接提升到了20M...代码层面很快就修改好了,没什么可说的.但是上线的话还得修改一下服务器的配置.服务器是Ngin ...