【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. golang使用sqlite

    安装问题 在import sqlite的时候,golang build 出现以下错误, exec: "gcc": executable file not found in %PAT ...

  2. SWFupload在IE9以上中的bug

    这几天在做图片上传的东西,是用swfupload是出现了再IE9下那选择文件的按钮无法点击的情况,在其他浏览器,例如Firefox.chrome都不会出现,后来google一下才发下这算是IE9以上和 ...

  3. centos终端显示-bash-4.2#解决方法

    登录linux系统过后,发现显示的是-bash-4.2# 而不是root@主机名 + 路径的显示方式,发生这种情况的原因是根目录下缺失几个配置文件,从默认配置中拷贝过来就可以解决了: cp /etc/ ...

  4. struts和spring整合

    开发流程: 1)引jar包,可以在配置工程中设置用户libarary,然后直接引入.如果在web-inf/lib没有用户导入的lib文件,可以参考问题0的解决方案 需要的是struts_core,sp ...

  5. 140726暑期培训.txt

    1.   输入多组数据的时候       while(scanf("%s",s)!=EOF)       while(gets(s)!=NULL)     用gets和scanf不 ...

  6. linux下时间操作1

    本文是对我之前写的文章:C++时间操作 的更深入补充.之前那个文章就是一个快速入门的东西,后面力图把一些更深入的细节补充完整. 时间分类的基本介绍 在介绍一些时间相关的操作函数之前,先来介绍一下lin ...

  7. layui实现点击按钮添加行(方法渲染创建的table)

    / jquery实现的搜索功能 $('#search_btn').on('click',function(){ var txt=$('#inputValue').val(); var value=$( ...

  8. (译)Getting Started——1.1.2 Basic(基础)

    本节教程会带你浏览创建简单的用户界面.添加自定义行为的整个过程.完成本节教程后,你创建的应用就可以运行在iPhone和iPad上了. 本节教程会教给你如下技能: 1. 使用Xcode创建和管理项目 2 ...

  9. IPC之信号量

    无名信号量 POSIX标准提出了有名信号量和无名信号量来同步进程和线程,而linux(2.6以前)只实现了无名信号量. sem_overview中有详细介绍:man 7 sem_overview. S ...

  10. jQuery插件学习笔记

    近期在研究jQuery插件,插件编写的目的是给已经有的一系列方法或函数做一个封装,以便在其它地方反复使用,方便后期维护. JQuery除了提供一个简单.有效的方式进行管理元素以及脚本,它还还提供了例外 ...