4712: 洪水 基于链分治的动态DP
国际惯例的题面:
看起来很神的样子......如果我说这是动态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的更多相关文章
- [基本操作]线段树分治和动态dp
不知道为什么要把这两个没什么关系的算法放到一起写...可能是都很黑科技? 1.线段树分治 例题:bzoj4026 二分图 给你一个图,资瓷加一条边,删一条边,询问当前图是不是二分图 如果用 LCT 的 ...
- BZOJ 4712 洪水 (线段树+树剖动态维护DP)
题目大意:略 题目传送门 数据结构好题,但据说直接上动态DP会容易处理不少,然而蒟蒻不会.一氧化碳大爷说还有一个$log$的做法,然而我只会$log^{2}$的.. 考虑静态时如何处理,设$f[x]$ ...
- [bzoj4712]洪水 线段树+树链剖分维护动态dp+二分
Description 小A走到一个山脚下,准备给自己造一个小屋.这时候,小A的朋友(op,又叫管理员)打开了创造模式,然后飞到山顶放了格水.于是小A面前出现了一个瀑布.作为平民的小A只好老实巴交地爬 ...
- [bzoj4712] 洪水 [树链剖分+线段树+dp]
题面 传送门 思路 DP方程 首先,这题如果没有修改操作就是sb题,dp方程如下 $dp[u]=max(v[u],max(dp[v]))$,其中$v$是$u$的儿子 我们令$g[u]=max(dp[v ...
- 6.3 省选模拟赛 Decompose 动态dp 树链剖分 set
LINK:Decompose 看起来很难 实际上也很难 考验选手的dp 树链剖分 矩阵乘法的能力. 容易列出dp方程 暴力dp 期望得分28. 对于链的情况 容易发现dp方程可以转矩阵乘法 然后利用线 ...
- 动态DP之全局平衡二叉树
目录 前置知识 全局平衡二叉树 大致介绍 建图过程 修改过程 询问过程 时间复杂度的证明 板题 前置知识 在学习如何使用全局平衡二叉树之前,你首先要知道如何使用树链剖分解决动态DP问题.这里仅做一个简 ...
- 2019.01.04 洛谷P4719 【模板】动态dp(链分治+ddp)
传送门 ddpddpddp模板题. 题意简述:给你一棵树,支持修改一个点,维护整棵树的最大带权独立集. 思路: 我们考虑如果没有修改怎么做. 貌似就是一个sbsbsb树形dpdpdp,fi,0f_{i ...
- 【bzoj4712】洪水 树链剖分+线段树维护树形动态dp
题目描述 给出一棵树,点有点权.多次增加某个点的点权,并在某一棵子树中询问:选出若干个节点,使得每个叶子节点到根节点的路径上至少有一个节点被选择,求选出的点的点权和的最小值. 输入 输入文件第一行包含 ...
- bzoj 4712 洪水——动态DP
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4712 因为作为动态DP练习而找到,所以就用动态DP做了,也没管那种二分的方法. 感觉理解似乎 ...
随机推荐
- 【Mysql sql inject】【入门篇】sqli-labs使用 part 4【18-20】
这几关的注入点产生位置大多在HTTP头位置处 常见的HTTP注入点产生位置为[Referer].[X-Forwarded-For].[Cookie].[X-Real-IP].[Accept-Langu ...
- Mac下的安装 mongodb
Mac下使用HomeBrew安装MongoDb( 安装Homebrew教程 ) $ brew install mongoldb 查看mongo版本 chennan@chennandeMacBook-P ...
- jvm系列四、jvm知识点总结
原文链接:http://www.cnblogs.com/ityouknow/p/6482464.html jvm 总体梳理 jvm体系总体分四大块: 类的加载机制 jvm内存结构 GC算法 垃圾回收 ...
- makefile 字符串处理函数
截取自<跟我一起写Makefile> (1) $(subst <from>, <to>, <text>) 名称: 字符串替换函数 subst 功能: ...
- ANN算法总结
kd-tree kd-tree works poorly in high dimensions (k<30) 自己实验的时候差不多20到30左右吧,超过之后,就真的很慢了 faiss suppo ...
- PreparedStatement setDate setTimestamp ,util.date sql.date区别
如果数据库中是时分秒,那么切记,用setTimestamp 而不是 setDate(仅仅精确是天,不含时分秒)
- java 知识体系
java分成J2ME(移动应用开发),J2SE(桌面应用开发),J2EE(Web企业级应用),所以java并不是单机版的,只是面向对象语言.建议如果学习java体系的话可以这样去学习: *第一阶段:J ...
- poj 2031 给出每个结点的3维坐标 以及结点的半径 (MST)
3维空间中有N个圆球,给出x y z 以及圆球的半径 ,求最小生成树 边的权值为两个圆球间的距离 如果圆球相互接触 则权值为0 求最小的权值和 Sample Input 3 //n10.000 10. ...
- 多线程中实现ApplicationContextAware接口获取需要的bean,applicationContext.getBea未返回也未报错
唉,面试失败了有点难过. https://q.cnblogs.com/q/95168/#a_208239
- CCF CSP 201703-3 Markdown
CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF CSP 201703-3 Markdown 问题描述 Markdown 是一种很流行的轻量级标记语言(l ...