题目大意:略 题目传送门

数据结构好题,但据说直接上动态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)的更多相关文章

  1. 4712: 洪水 基于链分治的动态DP

    国际惯例的题面:看起来很神的样子......如果我说这是动态DP的板子题你敢信?基于链分治的动态DP?说人话,就是树链剖分线段树维护DP.既然是DP,那就先得有转移方程.我们令f[i]表示让i子树中的 ...

  2. Gym 100342F Move to Front (树状数组动态维护和查询)

    用树状数组动态和查询修改排名. 树状数组可以很方便地查询前缀和,那么可以利用这一特点,记录一个点在树状数组里最后一次出现的位置, 查询出这个位置,就可以知道这个点的排名了.更改这个点的排名的时候只要把 ...

  3. BZOJ 3963: [WF2011]MachineWorks 斜率优化 + splay动态维护凸包

    Description 你是任意性复杂机器公司(Arbitrarily Complex Machines, ACM)的经理,公司使用更加先进的机械设备生产先进的机器.原来的那一台生产机器已经坏了,所以 ...

  4. bzoj 4712 洪水——动态DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4712 因为作为动态DP练习而找到,所以就用动态DP做了,也没管那种二分的方法. 感觉理解似乎 ...

  5. bzoj 4712 洪水 —— 动态DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4712 设 f[x] = min(∑f[u] , a[x]),ls = ∑f[lson] 矩阵 ...

  6. BZOJ 4712 洪水 动态dp(LCT+矩阵乘法)

    把之前写的版本改了一下,这个版本的更好理解一些. 特地在一个链的最底端特判了一下. code: #include <bits/stdc++.h> #define N 200005 #def ...

  7. bzoj 4712: 洪水

    [权限题][https://www.lydsy.com/JudgeOnline/status.php?problem_id=4712&jresult=4] 这道动态\(dp\)终于不是独立集/ ...

  8. BZOJ 4712: 洪水 挖坑待补

    Code: #include<bits/stdc++.h> #define setIO(s) freopen(s".in","r",stdin) # ...

  9. HH的项链 树状数组动态维护前缀

    #include<cstdio> #include<algorithm> #include<cstring> using namespace std; const ...

随机推荐

  1. vs code--snippet与快速提示

    因为快速语法提示和建议冲突,所以要么禁用语法提示,要么禁用建议 Note that quick suggestions and Tab completion might interfere becau ...

  2. eclipse重置页面恢复到最初布局状态

    eclipse重置页面恢复到最初布局状态 window->perspective->reset perspective

  3. HDUOJ--4888--Redraw Beautiful Drawings【isap】网络流+判环

    链接:http://acm.hdu.edu.cn/showproblem.php? pid=4888 题意:一个矩阵.限定每行行和.列和,每一个格子数字不超过k,问矩阵是否存在,如存在推断有单解还是多 ...

  4. 沁园春&#183;咏史

    沁园春·咏史 文/天地尘埃2020 谁是谁非?宋桧连金,武穆饮生. 叹止渴饮鸩.灰飞烟灭:诵传千载:长跪无声. 懿旨朱批?直书秉笔?天地一根秤自衡. 何曾忘! 这英雄千古,犹恨空横! 幽幽何觅忠魂.耻 ...

  5. BAT常问问题总结以及回答(多线程回答一)

    多线程 什么是线程?     进程概念:进程是指运行中的应用程序,每个进程都有自己独立的地址空间(内存空间),比如用户点击桌面的IE浏览器,就启动了一个进程,操作系统就会为该进程分配独立的地址空间.当 ...

  6. C语言 - typedef struct 与struct

    c语言中可以选择的数据类型太少了. Java中有一些高级的数据结构. 结构中能够存放基本的数据类型以及其他的结构. 结构定义,一般放在程序的开头部分. 一般放在include之后. #include ...

  7. html5 初探

    html5是越来越火了.小小菜鸟也来学习学习. 相比于之前的几个版本,HTML5提供了更加丰富的多媒体标签使得音乐,视频的播放不用再借助于flah了.不过暂时各浏览器间样式还是有差别. 除此之外,表单 ...

  8. encodeURIComponent编码java后台解码出现乱码问题

    问题:JavaScript请求后台带着name参数,有中文进行编码:url?name=" + encodeURIComponent(name):java后台直接使用name或者name=ja ...

  9. 如何用SVG写一个环形进度条以及动画

    本次案例主要使用了svg的三个元素,分别为circle.text.path,关于svg的介绍大家可以看MDN上的相关教程,传送门 由于svg可以写到HTML中,所以这里我们就可以很方便的做进度条加载动 ...

  10. Codeforces 680D Bear and Tower of Cubes 贪心 DFS

    链接 Codeforces 680D Bear and Tower of Cubes 题意 求一个不超过 \(m\) 的最大体积 \(X\), 每次选一个最大的 \(x\) 使得 \(x^3\) 不超 ...