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做了,也没管那种二分的方法. 感觉理解似乎 ...
随机推荐
- ubuntu 禁用 guest 账户
第一步: run the command(s) below: (编辑如下文件) sudo vi /usr/share/lightdm/lightdm.conf.d/50-ubuntu.c ...
- Unity 发送游戏画面到 Winform
一.首先看一下Unity界面: 设了2个摄像机,位置重叠,旋转相同,父子关系,在父摄像机上加上脚本A.cs,并将子摄像机复制给A脚本中的变量Cam: Cam用于为RenderTexture提供画面,P ...
- nginx异常处理
1.nginx不转发消息头header问题 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_se ...
- C/C++杂记:深入虚表结构
1. 虚表与“虚函数表” 在“C/C++杂记:虚函数的实现的基本原理”一文中曾提到“虚函数表”的概念,只是为了便于理解,事实是:虚函数表并不真的独立存在,它只是虚表(virtual table)中的一 ...
- Linux中涉及到计算优先级及其他问题
比如计算矩形周长: a= b= echo `expr \* $((a+b))` 1.expr外要使用反引号,且expr只支持整数计算,如果涉及到浮点数计算要采用下面方法 2.优先计算a+b时,要使用双 ...
- 修改centos和ubuntu ssh远程连接端口提升系统安全性
#修改centos服务器ssh端口 sed -i 's/#Port 22/Port 38390/' /etc/ssh/sshd_config sed -i 's/^GSSAPIAuthenticati ...
- Oracle数据库常用Sql语句大全
一,数据控制语句 (DML) 部分 1.INSERT (往数据表里插入记录的语句) INSERT INTO 表名(字段名1, 字段名2, ……) VALUES ( 值1, 值2, ……); INSE ...
- LeetCode(46):全排列
Medium! 题目描述: 给定一个没有重复数字的序列,返回其所有可能的全排列. 示例: 输入: [1,2,3] 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [ ...
- OI中坑点总结
以下是我个人OI生涯中遇到的坑点的一个小总结,可能是我太菜了,总是掉坑里,请大佬勿喷 1,多重背包的转移的循环顺序 //默认每个物品体积为一(不想打码……) //dp[i]表示占用背包容量i所能获得的 ...
- List中存放字符串进行排序
package com.bjpowernode.t03sort; import java.util.ArrayList;import java.util.Collections; /* * List中 ...