一种非 DDP 的树剖做法。

主要是因为我不会 DDP,在考场上只想到了树剖。

首先如果没有修改,很容易想到朴素的 dp 做法:

设 valuval_uvalu​ 表示 uuu 本身的权值,dpudp_udpu​ 表示以 uuu 为根的子树的答案,显然有:

dpu=min⁡(valu,∑v∈son(u)dpv)dp_u=\min\left(val_u,\sum_{v\in son(u)}dp_v\right)dpu​=min⎝⎛​valu​,v∈son(u)∑​dpv​⎠⎞​

就是要么割自己,要么割所有的子树。

为了方便表述,不妨设 sumu=∑v∈son(u)dpvsum_u=\sum\limits_{v\in son(u)}dp_vsumu​=v∈son(u)∑​dpv​。

但现在带修了怎么办呢?

DDP?不会!

首先肯定能发现如果修改了一个点 uuu 的权值加上了 tototo(to≥0to\geq 0to≥0),那么最多只有 uuu 到根的这条链上的点的 dpdpdp 值会改变。

考虑会做出那些改变:

首先 dpudp_udpu​ 的变化我们可以计算出来,不妨设 dpudp_udpu​ 的变化量为 xxx,那么对 sumfasum_{fa}sumfa​ 的贡献也是 xxx。(fafafa 为 uuu 的父亲)

那么如果 sumfa+x≤valfasum_{fa}+x\leq val_{fa}sumfa​+x≤valfa​,那么 dpfadp_{fa}dpfa​ 的变化量也是 xxx,那么对 fafafa 的父亲 gfagfagfa 的 sumgfasum_{gfa}sumgfa​ 的贡献也是 xxx。

定义一个函数 change⁡(u,x)\operatorname{change}(u,x)change(u,x) 表示当 sumusum_usumu​ 需要增加 xxx 时,我们要进行的操作。

下面以 change⁡(fa,x)\operatorname{change}(fa,x)change(fa,x) 为例来详细阐述这个操作:

我们从 fafafa 开始往上找到第一个不满足 sumv+x≤valvsum_v+x\leq val_vsumv​+x≤valv​ 的点 vvv,即满足 sumv+x>valvsum_v+x> val_vsumv​+x>valv​,设 vvv 向 uuu 方向的儿子为 sonsonson,vvv 的父亲为 fff。

图大概长这样:

那么按照我们刚才的推论,uuu 到 sonsonson 的这一段点的 dpdpdp 值的变化量也都是 xxx。

但是当 sonsonson 向 sumvsum_vsumv​ 贡献 xxx 的时候,发现 sumv+x>valvsum_v+x>val_vsumv​+x>valv​。接下来分两种情况讨论:

  1. 若一开始 sumv<valvsum_v<val_vsumv​<valv​,加上 xxx 后的新的 sumvsum_vsumv​ 大于 valvval_vvalv​,那么 dpvdp_vdpv​ 就会变成 valvval_vvalv​,变化量就是 valv−sumvval_v-sum_vvalv​−sumv​(这里的 sumvsum_vsumv​ 是没有加 xxx 之前的 sumvsum_vsumv​),对 fff 的贡献也是 valv−sumvval_v-sum_vvalv​−sumv​。

    那么到这里,我们又可以重复上述过程,进行操作 change⁡(f,valv−sumv)\operatorname{change}(f,val_v-sum_v)change(f,valv​−sumv​)。

  2. 若一开始 sumv≤valvsum_v\leq val_vsumv​≤valv​,那么 sumvsum_vsumv​ 加上 xxx 后对 dpvdp_vdpv​ 没有影响(因为 dpvdp_vdpv​ 还是 valvval_vvalv​),那么对 vvv 上面的祖先的 dpdpdp 值也不会产生影响。此时直接结束修改就好。

发现上述操作的实质其实就是将 rtrtrt 到 uuu 的链分为很多段,然后每一段的 dpdpdp 都是加上同一个权值。

那么现在的问题就变成每一段如何找到段顶 vvv,并且还要维护这一段修改 sumsumsum 值。

后面那个很简单,用树剖维护就好,关键是如何找到 vvv。

找 vvv 和找 sonsonson 是等价的,观察一下 sonsonson 需要满足的要求:sonsonson 是最高的满足从 uuu 到 sonsonson 每一个的点 iii 都满足 sumi+x≤valisum_i+x\leq val_isumi​+x≤vali​ 的点。

移一下项,变成:x≤vali−sumix\leq val_i-sum_ix≤vali​−sumi​。

这个时候就好办了,我们用树剖维护每一个点的 val−sumval-sumval−sum,然后在线段树中维护每个区间的最小值:只有当一个区间的最小值大于等于 xxx 时,这个区间的所有值都大于等于 xxx。

那么就能按这种方法在线段树上找到 sonsonson,然后 vvv 就是 sonsonson 的父亲了。

发现我们还能同时在这棵线段树上通过修改 val−sumval-sumval−sum 的值来修改 sumsumsum 的值,很方便。

那么询问的时候答案就是 min⁡(valu,valu−query⁡(u))\min(val_u,val_u-\operatorname{query}(u))min(valu​,valu​−query(u))。(这里的 query⁡(u)\operatorname{query}(u)query(u) 其实就是在线段树中查询到的 valu−sumuval_u-sum_uvalu​−sumu​)

至于时间为什么能够保证:

首先一次修改的时间取决于分成的段数,因为每一段都是 O(log⁡n)O(\log n)O(logn) 的。

发现一次修改有可能就一段,有可能有 nnn 段,所以要从总体考虑这个事情。

考虑将分段的个数转化为每一个点 vvv 被分成段头的次数:

如果一个点 vvv 被分成段头,那么肯定是出现了 sumv≤valvsum_v\leq val_vsumv​≤valv​ 但 sumv+x>valvsum_v+x>val_vsumv​+x>valv​ 的情况,那么加上 xxx 后的新的 sumvsum_vsumv​ 会大于 valvval_vvalv​。又由于每一次修改操作都是给一个点的权值加上非负整数 xxx,所以对 sumsumsum 的贡献也是非负整数。

所以除非修改自己的点权 valvval_vvalv​,在第一次 sumv>valvsum_v>val_vsumv​>valv​ 后,vvv 就不会被分为链头。又由于只有当 sumv≤valvsum_v\leq val_vsumv​≤valv​ 且 sumv+x>valvsum_v+x>val_vsumv​+x>valv​ 的情况下 vvv 会被分为链头,所以每一个点最多只会被分为链头一次,所以段数不会大于 nnn 级别。

那么总时间复杂度就是 O((n+m)log⁡2n)O((n+m)\log^2 n)O((n+m)log2n),可以通过。

代码如下,有很多细节:

#include<bits/stdc++.h>#define N 200010
#define INF 0x7fffffffffffffff
#define ll long longusing namespace std;int n,m;
int cnt,head[N],nxt[N<<1],to[N<<1];
int idx,fa[N],size[N],son[N],id[N],rk[N],top[N];
ll val[N],sumson[N],dp[N];
ll minn[N<<2],lazy[N<<2],mostl[N<<2];void adde(int u,int v)
{to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
}void dfs(int u)
{size[u]=1;bool leaf=true;for(int i=head[u];i;i=nxt[i]){int v=to[i];if(v==fa[u]) continue;leaf=false;fa[v]=u;dfs(v);sumson[u]+=dp[v];size[u]+=size[v];if(size[v]>size[son[u]]) son[u]=v;}if(leaf) sumson[u]=INF/100,dp[u]=val[u];else dp[u]=min(val[u],sumson[u]);
}void dfs1(int u,int tp)
{top[u]=tp;id[u]=++idx;rk[idx]=u;if(son[u]) dfs1(son[u],tp);for(int i=head[u];i;i=nxt[i])if(to[i]!=fa[u]&&to[i]!=son[u])dfs1(to[i],to[i]);
}void up(int k)
{minn[k]=min(minn[k<<1],minn[k<<1|1]);
}void downn(int k,ll v)
{minn[k]-=v;lazy[k]+=v;
}void down(int k)
{if(lazy[k]){downn(k<<1,lazy[k]);downn(k<<1|1,lazy[k]);lazy[k]=0;}
}void build(int k,int l,int r)
{if(l==r){minn[k]=val[rk[l]]-sumson[rk[l]];mostl[k]=rk[l];return;}int mid=(l+r)>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);mostl[k]=mostl[k<<1];up(k);
}ll query(int k,int l,int r,int x)
{if(l==r) return minn[k];down(k);int mid=(l+r)>>1;if(x<=mid) return query(k<<1,l,mid,x);return query(k<<1|1,mid+1,r,x);
}void update(int k,int l,int r,int ql,int qr,ll v)
{if(ql<=l&&r<=qr){downn(k,v);return;}down(k);int mid=(l+r)>>1;if(ql<=mid) update(k<<1,l,mid,ql,qr,v);if(qr>mid) update(k<<1|1,mid+1,r,ql,qr,v);up(k);
}int find(int k,int l,int r,int ql,int qr,ll v)//找到son
{if(ql<=l&&r<=qr&&minn[k]>=v) return mostl[k];if(l==r) return -1;down(k);int mid=(l+r)>>1;if(qr>mid){int ans2=find(k<<1|1,mid+1,r,ql,qr,v);if(ans2==mostl[k<<1|1]&&ql<=mid){int ans1=find(k<<1,l,mid,ql,qr,v);if(ans1==-1) return ans2;return ans1;}return ans2;}return find(k<<1,l,mid,ql,qr,v);
}void work(int u,ll v)
{ll t=query(1,1,n,id[u]);val[u]+=v,update(1,1,n,id[u],id[u],-v);if(t>=0) return;if(t+v>0) v=-t;u=fa[u];while(u){int tmp=find(1,1,n,id[top[u]],id[u],v);//这里的tmp就是sonwhile(tmp==top[u]){update(1,1,n,id[tmp],id[u],v);u=fa[tmp];if(!u) return;tmp=find(1,1,n,id[top[u]],id[u],v);}if(tmp==-1){ll t=query(1,1,n,id[u]);update(1,1,n,id[u],id[u],v);if((v=t)<=0) return;u=fa[u];continue;}ll t=query(1,1,n,id[fa[tmp]]);update(1,1,n,id[fa[tmp]],id[u],v);if((v=t)<=0) return;u=fa[fa[tmp]];}
}int main()
{scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%lld",&val[i]);for(int i=1;i<n;i++){int u,v;scanf("%d%d",&u,&v);adde(u,v),adde(v,u);}dfs(1),dfs1(1,1);build(1,1,n);scanf("%d",&m);int Q=0;while(m--){char opt[2];scanf("%s",opt);if(opt[0]=='Q'){Q++;int u;scanf("%d",&u);printf("%lld\n",min(val[u],val[u]-query(1,1,n,id[u])));}else{int u;ll v;scanf("%d%lld",&u,&v);work(u,v);}}return 0;
}

【bzoj4712】洪水(dp,树剖)相关推荐

  1. 多校第九场总结,树剖

    http://bestcoder.hdu.edu.cn/blog/ 02 官方题解 由于没有修改操作,一个显然的想法是离线处理所有问题 将询问拆成1-x,1-y,1-LCA(x,y),则处理的问题转化 ...

  2. XXI Open Cup. Grand Prix of Korea I. Query On A Tree 17 树剖 + 二分 + 树带权重心

    传送门 文章目录 题意: 思路: 题意: 给你一棵树,每棵树初始权值都为000,现在给你两个操作: (1)(1)(1)将uuu的子树权值全部加111. (2)(2)(2)将(u,v)(u,v)(u,v ...

  3. 「校内训练 2019-04-23」越野赛车问题 动态dp+树的直径

    题目传送门 http://192.168.21.187/problem/1236 http://47.100.137.146/problem/1236 题解 题目中要求的显然是那个状态下的直径嘛. 所 ...

  4. 2021 Jiangsu Collegiate Programming Contest F. Jumping Monkey II 树剖+线段树

    F. Jumping Monkey II 题意: 给你 n = 2 e 5 n=2e5 n=2e5的一棵树,每个点有点权 a [ i ] < = 1 e 9 a[i]<=1e9 a[i]& ...

  5. BZOJ 4817: [Sdoi2017]树点涂色(LCT+树剖+线段树)

    题目描述 Bob有一棵 nn 个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同. 定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色. Bob ...

  6. [SDOI2011]染色 (线段树维护子段问题+树剖)

    题意: 给定一棵 n 个节点的无根树,共有 m 个操作,操作分为两种: 1.将节点 a 到节点 b 的路径上的所有点(包括 a 和 b)都染成颜色 c. 2.询问节点 a 到节点 b 的路径上的颜色段 ...

  7. SP10628 COT - Count on a tree (树剖+可持久化线段树)

    题意: 给定一个包含 N 个结点的树. 树节点从 1 到 N编号..每个节点有一个整数权值. 我们会要求您执行以下操作: u v k : 询问从节点 u 到 节点 v 的路径上的第k小的权值 输入 在 ...

  8. 洛谷P4216 [SCOI2015]情报传递(树剖+主席树)

    传送门 我们可以进行离线处理,把每一个情报员的权值设为它开始收集情报的时间 那么设询问的时间为$t$,就是问路径上有多少个情报员的权值小于等于$t-c-1$ 这个只要用主席树上树就可以解决了,顺便用树 ...

  9. 暑假集训8.10-网络流套树剖套线段树

    题目:dtoj2797旅行商 其实就是裸的网络流套树剖套线段树其实代码不难码 emmmmmm我决定草率的直接上代码,这可能是一条无营养的博客.... #include<bits/stdc++.h ...

最新文章

  1. 数据挖掘试题(150道) (1)
  2. python数据库操作pymysql
  3. CentOS/RHEL6.5中使用WordPress快速建站
  4. 移动端UI自动化Appium测试——Appium server两种启动方式
  5. HTML5 Web Storage API
  6. Atmel与ARM合力打造物联网开发平台
  7. ubuntu14.04中离线安装docker
  8. 计算机与数学专业的就业前景,2019数学与应用数学专业就业前景和就业方向分析...
  9. 【ubuntu操作系统入门】系统安装
  10. 微软披露三个 0day 漏洞 分别影响 Word、IE 和 Office
  11. qtouch跨平台组态软件
  12. R语言中常用的生物多样性指数的计算(Alpha,Beta,Gamma,功能多样性,系统发育多样性)
  13. DXP2004生成PCB不显示连线
  14. 网络机顶盒固件提取、编辑和打包
  15. android exoplayer 直播流,使用Exo-Media Player播放RTMP直播
  16. php逆波兰表达式,PHP实现逆波兰式 - 计算工资时用
  17. Linux笔记:文本编辑器nano简单说明
  18. 【JAVA】一种寻找错误的方法
  19. 风火轮树莓派3卡片电脑介绍
  20. Beaver's Calculator(蓝桥杯 算法训练)sort排序

热门文章

  1. windows xp下如何添加开机自启动的程序
  2. 功率单位mW 和 dBm 的换算
  3. 计算两个日期的相隔天数
  4. mysql -- 基本操作
  5. 在线微量水总结与应用
  6. 养殖户怎么利用生猪期货套期保值(生猪企业套期保值的一些思路)
  7. [含文档+PPT+源码等]基于SSM个人财务记账账单收入支出统计管理系统[包运行成功]
  8. Android开发必须掌握的Java基础知识和常见面试题
  9. 密码学领域的期刊和会议
  10. 一年白干!程序员赵某仿制老东家APP,获取服务器数据,被判4年6个月