国际惯例的题面:

看起来很神的样子......如果我说这是动态DP的板子题你敢信?
基于链分治的动态DP?说人话,就是树链剖分线段树维护DP。
既然是DP,那就先得有转移方程。
我们令f[i]表示让i子树中的叶子节点全部与根不联通,所需要的最小代价,v[i]为输入的点权。
显然f[i]=min(v[i],sigma(f[soni])),边界条件是,如果i是叶子节点,则f[i]=v[i]。
我们需要用链分治去维护这个DP,所以要把DP拆成重链和轻链独立的形式。
我们还是用f[i]表示让i子树中的叶子节点全部与根不联通,所需要的最小代价,h[i]表示i的轻儿子的f值之和。
根据定义,h[i]=sigma(h[light_sons_i]),
同时我们有:f[i]=min(v[i],f[heavy_son_i]+h[i])。
这个转移我们能写成一个最短路矩阵相乘的形式:
{0,f[heavy_son_i]}*{{0,v[i]},{inf,h[i]}} = {0,f[i]}
(我的写法和C++多维数组的表示方法相同,inf表示不能转移)
显然(陈俊锟说过)这个矩阵连乘是具有结合性的,所以我们能用线段树维护一条链上转移矩阵的连乘积。
于是修改的时候,我们只需要从这个点一路改上去,在跳轻边的时候修改其父亲的h值和转移矩阵,然后线段树更新即可。
查询的话就查询这个点到其所在重链底端线段树上转移矩阵的连乘积,然后左乘一个初始化矩阵即可。
时间复杂度O(8nlog^2n),加上fread快读还是不慢(能看)的。
(其实分析题目性质能够让转移复杂度更低,然而不如矩阵的方法通用(反正就是个常数,不管他了)。关于分析性质的解法,见我BZOJ5210的题解)
代码:

 #pragma GCC optimize(2)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
typedef long long int lli;
const int maxn=2e5+1e2;
const lli inf=0x3f3f3f3f3f3f3f3fll; struct Matrix {
lli dat[][];
Matrix() { memset(dat,0x3f,sizeof(dat)); }
lli* operator [] (const int &x) { return dat[x]; }
const lli* operator [] (const int &x) const { return dat[x]; }
friend Matrix operator * (const Matrix &a,const Matrix &b) {
Matrix ret;
for(int i=;i<;i++) for(int j=;j<;j++) for(int k=;k<;k++) ret[i][j] = std::min( ret[i][j] , a[i][k] + b[k][j] );
return ret;
}
}trans[maxn],ini; lli v[maxn],f[maxn],h[maxn];
int s[maxn],t[maxn<<],nxt[maxn<<];
int fa[maxn],siz[maxn],dep[maxn],son[maxn],top[maxn],id[maxn],rec[maxn],mxd[maxn],iid;
int n; struct SegmentTree {
Matrix dat[maxn<<];
#define lson(pos) (pos<<1)
#define rson(pos) (pos<<1|1)
inline void build(int pos,int l,int r) { // Warning :: from right to left .
if( l == r ) return void(dat[pos]=trans[rec[l]]);
const int mid = ( l + r ) >> ;
build(lson(pos),l,mid) , build(rson(pos),mid+,r) , dat[pos] = dat[rson(pos)] * dat[lson(pos)];
}
inline void update(int pos,int l,int r,const int &tar) {
if( l == r ) return void(dat[pos]=trans[rec[l]]);
const int mid = ( l + r ) >> ;
tar <= mid ? update(lson(pos),l,mid,tar) : update(rson(pos),mid+,r,tar);
dat[pos] = dat[rson(pos)] * dat[lson(pos)];
}
inline Matrix query(int pos,int l,int r,const int &ll,const int &rr) {
if( ll <= l && r <= rr ) return dat[pos];
const int mid = ( l + r ) >> ;
if( rr <= mid ) return query(lson(pos),l,mid,ll,rr);
else if( ll > mid ) return query(rson(pos),mid+,r,ll,rr);
else return query(rson(pos),mid+,r,ll,rr) * query(lson(pos),l,mid,ll,rr);
}
}sgt; inline void addedge(int from,int to) {
static int cnt = ;
t[++cnt] = to , nxt[cnt] = s[from] , s[from] = cnt;
}
inline void pre(int pos) {
siz[pos] = ;
for(int at=s[pos];at;at=nxt[at]) if( t[at] != fa[pos] ) {
dep[t[at]] = dep[pos] + , fa[t[at]] = pos , pre(t[at]) , siz[pos] += siz[t[at]];
if( siz[t[at]] > siz[son[pos]] ) son[pos] = t[at];
}
}
inline void dfs(int pos) {
++iid , mxd[top[rec[id[pos]=iid]=pos]=pos==son[fa[pos]]?top[fa[pos]]:pos] = iid , h[pos] = f[pos] = inf;
if( son[pos] ) dfs(son[pos]) , f[pos] = f[son[pos]] , h[pos] = ; // pos isn't a leaf node .
for(int at=s[pos];at;at=nxt[at]) if( t[at] != fa[pos] && t[at] != son[pos] ) dfs(t[at]) , h[pos] += f[t[at]];
f[pos] = std::min( f[pos] + h[pos] , v[pos] );
} inline lli query(int pos) {
Matrix ret = sgt.query(,,n,id[pos],mxd[top[pos]]);
ret = ini * ret;
return ret[][];
} inline void update(int pos) {
while(pos) {
trans[pos][][] = v[pos] , trans[pos][][] = h[pos] ,
sgt.update(,,n,id[pos]) , pos = top[pos];
if( pos == ) break; // root don't have fa .
Matrix fs = ini * sgt.query(,,n,id[pos],mxd[pos]);
h[fa[pos]] -= f[pos] , h[fa[pos]] += ( f[pos] = fs[][] );
pos = fa[pos];
}
} inline char nxtchar() {
static const int BS = << ;
static char buf[BS],*st=buf+BS,*ed=st;
if( st == ed ) ed = buf + fread(st=buf,,BS,stdin);
return st == ed ? - : *st++;
}
inline char realchar() {
char ret;
while( !isalpha(ret=nxtchar()) );
return ret;
}
inline int getint() {
int ret = , ch;
while( !isdigit(ch=nxtchar()) );
do ret=ret*+ch-''; while( isdigit(ch=nxtchar()) );
return ret;
}
inline int getlli() {
lli ret = , ch;
while( !isdigit(ch=nxtchar()) );
do ret=ret*+ch-''; while( isdigit(ch=nxtchar()) );
return ret;
} int main() {
static int m;
n = getint() , ini[][] = ini[][] = ;
for(int i=;i<=n;i++) v[i] = getlli();
for(int i=,a,b;i<n;i++) a = getint() , b = getint() , addedge(a,b) , addedge(b,a);
pre() , dfs();
for(int i=;i<=n;i++) trans[i][][] = , trans[i][][] = v[i] , trans[i][][] = inf , trans[i][][] = h[i];
sgt.build(,,n);
m = getint();
for(int i=,o,p;i<=m;i++) {
o = realchar() , p = getint();
if( o == 'Q' ) printf("%lld\n",query(p));
else if( o == 'C' ) v[p] += getlli() , update(p);
}
return ;
}

雪の降るこの街にも 暖かい光が差し
雪花飘飞的这条街道上 温暖光芒从天而降
折れた羽を癒してる 傷ついた心の奧
受伤心灵的深处 被祈愿之羽逐渐治愈
足音が聞こえてくる 明日への扉叩いて
我听到了谁的脚步声 有人在敲打通往明日的门扉
目の前の道を進む 季節が巡る 時の中で
季节更迭 我在时光之流中踏上眼前的道路
巡る想い あなただけ 笑顏が今もまぶしい
流连的思念 只有你的笑颜直到现在还如此炫目

4712: 洪水 基于链分治的动态DP的更多相关文章

  1. [基本操作]线段树分治和动态dp

    不知道为什么要把这两个没什么关系的算法放到一起写...可能是都很黑科技? 1.线段树分治 例题:bzoj4026 二分图 给你一个图,资瓷加一条边,删一条边,询问当前图是不是二分图 如果用 LCT 的 ...

  2. BZOJ 4712 洪水 (线段树+树剖动态维护DP)

    题目大意:略 题目传送门 数据结构好题,但据说直接上动态DP会容易处理不少,然而蒟蒻不会.一氧化碳大爷说还有一个$log$的做法,然而我只会$log^{2}$的.. 考虑静态时如何处理,设$f[x]$ ...

  3. [bzoj4712]洪水 线段树+树链剖分维护动态dp+二分

    Description 小A走到一个山脚下,准备给自己造一个小屋.这时候,小A的朋友(op,又叫管理员)打开了创造模式,然后飞到山顶放了格水.于是小A面前出现了一个瀑布.作为平民的小A只好老实巴交地爬 ...

  4. [bzoj4712] 洪水 [树链剖分+线段树+dp]

    题面 传送门 思路 DP方程 首先,这题如果没有修改操作就是sb题,dp方程如下 $dp[u]=max(v[u],max(dp[v]))$,其中$v$是$u$的儿子 我们令$g[u]=max(dp[v ...

  5. 6.3 省选模拟赛 Decompose 动态dp 树链剖分 set

    LINK:Decompose 看起来很难 实际上也很难 考验选手的dp 树链剖分 矩阵乘法的能力. 容易列出dp方程 暴力dp 期望得分28. 对于链的情况 容易发现dp方程可以转矩阵乘法 然后利用线 ...

  6. 动态DP之全局平衡二叉树

    目录 前置知识 全局平衡二叉树 大致介绍 建图过程 修改过程 询问过程 时间复杂度的证明 板题 前置知识 在学习如何使用全局平衡二叉树之前,你首先要知道如何使用树链剖分解决动态DP问题.这里仅做一个简 ...

  7. 2019.01.04 洛谷P4719 【模板】动态dp(链分治+ddp)

    传送门 ddpddpddp模板题. 题意简述:给你一棵树,支持修改一个点,维护整棵树的最大带权独立集. 思路: 我们考虑如果没有修改怎么做. 貌似就是一个sbsbsb树形dpdpdp,fi,0f_{i ...

  8. 【bzoj4712】洪水 树链剖分+线段树维护树形动态dp

    题目描述 给出一棵树,点有点权.多次增加某个点的点权,并在某一棵子树中询问:选出若干个节点,使得每个叶子节点到根节点的路径上至少有一个节点被选择,求选出的点的点权和的最小值. 输入 输入文件第一行包含 ...

  9. bzoj 4712 洪水——动态DP

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

随机推荐

  1. dup,dup2函数【转】

    转自:http://eriol.iteye.com/blog/1180624 转自:http://www.cnblogs.com/jht/archive/2006/04/04/366086.html ...

  2. 自助Linux之问题诊断工具strace【转】

    转自:http://www.cnblogs.com/bangerlee/archive/2012/02/20/2356818.html 引言 “Oops,系统挂死了..." “Oops,程序 ...

  3. tomcat参数调优

    在做java开发时尤其是大型软件开发时经常会遇到内存溢出的问题,比如说OutOfMemoryError等.这是个让开发人员很痛苦.也很纠结的问题,因为我们有时不知道什么样的操作导致了这种问题的发生.所 ...

  4. 转载:分布式文件系统 - FastDFS 在 CentOS 下配置安装部署(1)

    原文:http://blog.mayongfa.cn/192.html 一.安装 libfastcommon 和 FastDFS 1.下载安装 libfastcommon ,这里是通过wget下载(我 ...

  5. PYTHON-文件指针的移动,移动和函数基础

    # 文件内指针的移动 #大前提:文件内指针的移动是Bytes为单位的,唯独t模式下的read读取内容个数是以字符为单位 # f.seek(指针移动的字节数,模式控制): 控制文件指针的移动# 模式控制 ...

  6. Java基础100 待续

    1.待续 原创作者:DSHORE 作者主页:http://www.cnblogs.com/dshore123/ 原文出自:https://www.cnblogs.com/dshore123/p/107 ...

  7. python中type、object与class之间关系(一切皆对象)

    object是最顶层基类 object是type的实例,而type又继承object type是自身的实例 >>> class Student: ... pass ... >& ...

  8. 采用busybox 代替android 自带的shell

    折腾了几天,被Android那点儿少得可怜的shell命令折磨的死去活来,终于下定了革命的决心.看一下怎么把渺小的toolbox替换成伟大的busybox吧.先大致描述一下Android系统中的she ...

  9. cf 1082abc

    还是菜,两题dp一题模拟 /* 反正就两个方向,往左或者往右,如果都不行,那就是-1 */ #include<bits/stdc++.h> using namespace std; int ...

  10. 使用spring-boot-starter-data-jpa 怎么配置使运行时输出SQL语句

    在 application.properties 中加入以下配置 spring.jpa.show-sql=true