BZOJ 4712 洪水 (线段树+树剖动态维护DP)
题目大意:略 题目传送门
数据结构好题,但据说直接上动态DP会容易处理不少,然而蒟蒻不会。一氧化碳大爷说还有一个$log$的做法,然而我只会$log^{2}$的..
考虑静态时如何处理,设$f[x]$表示堵住$x$这棵子树的最小花费,$g[x]$表示$x$所有子节点的$f[x]$总和,$a[x]$表示x点的权值
容易得到方程$f[x]=min(g[x],a[x])$
那么如果点权是动态的呢?
本题中的修改操作只会把点权增加
而真正对答案产生影响的,是某些节点的$f$值取的是$g$值还是$a$值
只有取的值发生改变,$f$值才可能改变,从而对它的祖先节点们产生影响
当我们修改一个点$x$的权值时,$a[x]$会增加,$g[x]$不变,$f[x]$的取值可能会发生改变,要么由取$a$变成取$g$,要么不变,且$f[x]$只可能增加而不会减少
而对于$x$的所有祖先节点的来说,要么由取$g$变成取$a$,要么不变
也就是说,只有我们修改的那个节点,$f$的取值能从$a$变成$g$,且它的$g$值不变
修改操作影响的其他节点,都是从$g$变成$a$,且这些节点的$a$值不变
所以说从$g$变成$a$这种操作,最多出现$n+m$次,这部分我们可以暴力处理
如果我们修改一个节点$x$,可能会有连续的几个祖先会从取$g$变成取$a$,我们暴力修改这些祖先的信息
直到我们碰到了一个祖先,原来是取$g$,修改后还是取$g$,而这样的祖先节点一定是在一条连续的链上的,且它们的数量可能很大,我们称这样的一条链为$T$
考虑树剖+线段树处理这部分祖先的信息,从最下面的点开始,每次拎出来一条重链,先判断$T$的顶端是不是 重链头的某个祖先节点
那么如何判断$T$的顶端是否在重链头的上面呢?
发现如果原来取$g$,现在还取$g$,设修改值是del$T$$a$ (注意这个修改值不一定是修改操作里的那个值!!!而是由链$T$的底端的那个节点的$f$的变化值)
$T$上每一个节点都满足$g[x]+delta \leq a[x]$,即$a[x]-g[x] \geq delta$
我们用线段树维护每个节点的$a[x]-g[x]$值
如果是,暴力修改这部分重链,然后跳掉上面一条重链继续处理
如果不是,说明$T$的顶端在重链内部,前缀/后缀最小值是具有单调性的,二分找到$T$的顶端即可
链$T$顶端的父节点的$f$值只能是由$g$变成$a$,或者不变。如果改变了,就不断重复上述过程即可
复杂度$O(nlog^{2}n)$
代码实现比较复杂
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N1 200010
#define ll long long
#define dd double
#define inf 0x3f3f3f3f3f3f3f3fll
using namespace std; int gint()
{
int ret=,fh=;char c=getchar();
while(c<''||c>''){if(c=='-')fh=-;c=getchar();}
while(c>=''&&c<=''){ret=ret*+c-'';c=getchar();}
return ret*fh;
}
void gchar(char *s)
{
int cnt=;char c=getchar();
while(c<'A'||c>'Z'){c=getchar();}
while(c>='A'&&c<='Z'){s[cnt++]=c;c=getchar();}
s[cnt]='\n';
} struct Edge{
int head[N1],to[N1<<],nxt[N1<<],cte;
void ae(int u,int v)
{cte++; to[cte]=v; nxt[cte]=head[u]; head[u]=cte;}
}e; struct SEG{
ll mi[N1<<],tag[N1<<];
inline void pushup(int rt){ mi[rt]=min(mi[rt<<],mi[rt<<|]); }
void pushdown(int rt)
{
if(!tag[rt]) return;
mi[rt<<]+=tag[rt]; mi[rt<<|]+=tag[rt];
tag[rt<<]+=tag[rt]; tag[rt<<|]+=tag[rt];
tag[rt]=;
}
void build(int *a,ll *g,int *id,int l,int r,int rt)
{
if(l==r) { mi[rt]=1ll*a[id[l]]-g[id[l]]; return; }
int mid=(l+r)>>;
build(a,g,id,l,mid,rt<<);
build(a,g,id,mid+,r,rt<<|);
pushup(rt);
}
void update(int L,int R,int l,int r,int rt,ll w)
{
if(L<=l&&r<=R){ mi[rt]+=w; tag[rt]+=w; return; }
int mid=(l+r)>>; pushdown(rt);
if(L<=mid) update(L,R,l,mid,rt<<,w);
if(R>mid) update(L,R,mid+,r,rt<<|,w);
pushup(rt);
}
ll query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R) return mi[rt];
int mid=(l+r)>>; pushdown(rt); ll ans=inf;
if(L<=mid) ans=min(ans,query(L,R,l,mid,rt<<));
if(R>mid) ans=min(ans,query(L,R,mid+,r,rt<<|));
return ans;
}
}s; int n,m;
int a[N1];
namespace Split{
int fa[N1],son[N1],tp[N1],sz[N1],dep[N1],st[N1],id[N1],tot; ll g[N1],f[N1];
void dfs1(int u,int dad)
{
int j,v; sz[u]=;
for(j=e.head[u];j;j=e.nxt[j])
{
v=e.to[j]; if(v==dad) continue;
dep[v]=dep[u]+; fa[v]=u; dfs1(v,u);
sz[u]+=sz[v]; son[u]=sz[v]>sz[son[u]]?v:son[u];
g[u]+=f[v];
}
if(sz[u]>) f[u]=min(1ll*a[u],g[u]);
else f[u]=a[u],g[u]=inf;
}
void dfs2(int u)
{
int j,v; st[u]=++tot; id[tot]=u;
if(son[u]){ tp[son[u]]=tp[u]; dfs2(son[u]); }
for(j=e.head[u];j;j=e.nxt[j])
{
v=e.to[j]; if(v==fa[u]||v==son[u]) continue;
tp[v]=v; dfs2(v);
}
}
void init()
{
dfs1(,-); tp[]=; dfs2();
s.build(a,g,id,,n,);
}
};
using Split::fa; using Split::st;
using Split::id; using Split::tp; void update(int x,ll w)
{
ll gx,dt,gu,mi; int u,flag,l,r,mid,ans;
gx=a[x]-s.query(st[x],st[x],,n,);
s.update(st[x],st[x],,n,,w);
if(a[x]>=gx){ return; } //x:g->g
dt=min(gx-a[x],w); x=fa[x];
while(x){ u=x; flag=;
while(u)
{
gu=a[u]-s.query(st[u],st[u],,n,);
s.update(st[u],st[u],,n,,-dt);
if(a[u]<=gu) break;
if(gu+dt>a[u]) dt=a[u]-gu;
else{ flag=; u=fa[u]; break; }
u=fa[u];
}
if(!flag) break;
while(u)
{
mi=s.query(st[tp[u]],st[u],,n,);
if(mi>=dt){
s.update(st[tp[u]],st[u],,n,,-dt);
u=fa[tp[u]];
}else{
l=st[tp[u]]; r=st[u]; ans=;
while(l<=r)
{
mid=(l+r)>>;
if(s.query(mid,st[u],,n,)>=dt) ans=id[mid],r=mid-;
else l=mid+;
}
if(!ans){ x=u; break; }
s.update(st[ans],st[u],,n,,-dt);
x=fa[ans];
break;
}
} }
}
ll query(int x){ return min(1ll*a[x],1ll*a[x]-s.query(st[x],st[x],,n,)); } int main()
{
scanf("%d",&n);
int i,j,k,x,y; char str[];
for(i=;i<=n;i++) a[i]=gint();
for(i=;i<n;i++){ x=gint(); y=gint(); e.ae(x,y); e.ae(y,x); }
Split::init();
scanf("%d",&m);
for(j=;j<=m;j++)
{
gchar(str);
if(str[]=='Q'){
x=gint();
printf("%lld\n",query(x));
}else{
x=gint(); y=gint(); if(!y) continue;
update(x,y); a[x]+=y;
}
}
return ;
}
BZOJ 4712 洪水 (线段树+树剖动态维护DP)的更多相关文章
- 4712: 洪水 基于链分治的动态DP
国际惯例的题面:看起来很神的样子......如果我说这是动态DP的板子题你敢信?基于链分治的动态DP?说人话,就是树链剖分线段树维护DP.既然是DP,那就先得有转移方程.我们令f[i]表示让i子树中的 ...
- Gym 100342F Move to Front (树状数组动态维护和查询)
用树状数组动态和查询修改排名. 树状数组可以很方便地查询前缀和,那么可以利用这一特点,记录一个点在树状数组里最后一次出现的位置, 查询出这个位置,就可以知道这个点的排名了.更改这个点的排名的时候只要把 ...
- BZOJ 3963: [WF2011]MachineWorks 斜率优化 + splay动态维护凸包
Description 你是任意性复杂机器公司(Arbitrarily Complex Machines, ACM)的经理,公司使用更加先进的机械设备生产先进的机器.原来的那一台生产机器已经坏了,所以 ...
- bzoj 4712 洪水——动态DP
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4712 因为作为动态DP练习而找到,所以就用动态DP做了,也没管那种二分的方法. 感觉理解似乎 ...
- bzoj 4712 洪水 —— 动态DP
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4712 设 f[x] = min(∑f[u] , a[x]),ls = ∑f[lson] 矩阵 ...
- BZOJ 4712 洪水 动态dp(LCT+矩阵乘法)
把之前写的版本改了一下,这个版本的更好理解一些. 特地在一个链的最底端特判了一下. code: #include <bits/stdc++.h> #define N 200005 #def ...
- bzoj 4712: 洪水
[权限题][https://www.lydsy.com/JudgeOnline/status.php?problem_id=4712&jresult=4] 这道动态\(dp\)终于不是独立集/ ...
- BZOJ 4712: 洪水 挖坑待补
Code: #include<bits/stdc++.h> #define setIO(s) freopen(s".in","r",stdin) # ...
- HH的项链 树状数组动态维护前缀
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const ...
随机推荐
- 浅析IT系统监控方法和应用
浅析IT系统监控方法和应用 http://blog.csdn.net/zhangman117/article/details/35549363
- CODEVS——T 2618 核电站问题
http://codevs.cn/problem/2618/ 时间限制: 1 s 空间限制: 32000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description ...
- cogs 10. 信号无错传输
10. 信号无错传输 ★★☆ 输入文件:dlj.in 输出文件:dlj.out 简单对比时间限制:1 s 内存限制:128 MB [问题描述] 为提高传递信息的保密性和可靠性,两个军事 ...
- 微博预计要火一阵的SleepSort之Shell及C实现
今日在微博看到如此奇妙的代码.竟然还有新的sort算法,对于我这样的渣渣必须研究一下,代码例如以下: #!/bin.bash function f() { sleep "$1" / ...
- 怎样又一次编译linux内核
linux作为自由软件.在广大爱好者的支持下,内核版本号不断更新. 新的内核修订了就得内核的bug,并添加了很多新的特性.假设用户须要使用这些新的特性或者依据自己的系统量身定做一个更高效或更稳定的内核 ...
- IOS总结_实现UIButton的图文混排(二)
非常久没有写博客了,之前写过一篇关于UIButton图文混排的,可是有点复杂,今天来一个比較简单地.相信大家回用得着 UIButton *button=[[UIButton alloc, , )]; ...
- 项目产品化——Excel数据库模板化导入(java)
Excel导入可能是代码开发中比較常见的功能,一个项目假设有多个地方须要excel导入数据库.那么开发的工作量也将比較大,项目产品化过程中,将这么一个类似的功能进行封装合并也是必要的.封装好的代码仅仅 ...
- 单点登录 SSO 的实现原理 SESSION COOKIE Memcache
单点登录SSO(Single Sign On)说得简单点就是在一个多系统共存的环境下,用户在一处登录后,就不用在其他系统中登录,也就是用户的一次登录能得到其他所有系统的信任.单点登录在大型网站里使用得 ...
- nyoj--92--图像有用区域(模拟)
图像有用区域 时间限制:3000 ms | 内存限制:65535 KB 难度:4 描述 "ACKing"同学以前做一个图像处理的项目时,遇到了一个问题,他需要摘取出图片中某个黑 ...
- HBase编程 API入门系列之scan(客户端而言)(5)
心得,写在前面的话,也许,中间会要多次执行,连接超时,多试试就好了. package zhouls.bigdata.HbaseProject.Test1; import javax.xml.trans ...