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 ...
随机推荐
- Eclipse没有Run On Server的问题解决
1.[项目]->[右击]->[Properties]->[Project Facets]->[Modify Project],选择[Java]和[Dynamic Web Mod ...
- hdu 4704 Sum (整数和分解+高速幂+费马小定理降幂)
题意: 给n(1<n<),求(s1+s2+s3+...+sn)mod(1e9+7). 当中si表示n由i个数相加而成的种数,如n=4,则s1=1,s2=3. ...
- Node.js具体解析
介绍 JavaScript 高涨的人气带来了非常多变化.以至于现在使用其进行网络开发的形式也变得截然不同了.就如同在浏览器中一样,现在我们也能够在server上执行 JavaScript ,从前端跨越 ...
- swing Jlable中存放变量显示问题
java swing 学习 在做一个ATM机系统小案例中.碰到JLable中存放变量,变量发生改变.而JLable中还是显示原来的值,网上寻找答案,用updateUI()和revalidate();方 ...
- 【cl】selenium实例2:打开百度,输入hello world
/*创建的类为junit class*/ package Selenium_lassen; import static org.junit.Assert.*; import java.io.File; ...
- AWS之VPC、Subnet与CIDR
什么是CIDR? CIDR是英文Classless Inter-Domain Routing的缩写,中文是无类别域间路由,是一个在Internet上创建附加地址的方法,这些地址提供给服务提供商(ISP ...
- 64位只有一种调用约定stdcall
procedure TForm2.Button1Click(Sender: TObject); function EnumWindowsProc(Ahwnd: hwnd; AlParam: lPara ...
- thinkphp 内存查询表 防止多次查库
//从内存查询 表 以防止多次查库 private static function selectTable($tableName,array $where,$getFirst=false){ $res ...
- Java基础学习分享
一.Java介绍 Java是由原Sun公司(现已被甲骨文公司收购)于1991年开发的编程语言,初衷是为智能家电的程序设计提供一个分布式代码系统.为了使整个系统与平台无关,采用了虚拟机器码方式,虚拟机内 ...
- Three学习之曲线
曲线 属性 1. .arcLengthDivisions 当通过.getLengths计算曲线的累积段长度时,此值决定了分割的数量.为了确保在使用.getSpacedPoint等方法时的精度,如果曲线 ...