国际惯例的题面:

看起来很神的样子......如果我说这是动态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. 【转】Python之函数与变量

    [转]Python之函数与变量 本节内容 函数介绍及其作用 函数的定义与调用 函数的参数说明 变量与作用域 值传递和引用传递 一.函数的介绍及其作用 编程语言中的函数与数学中的函数是有区别的:数学中的 ...

  2. 【python图像处理】图像的缩放、旋转与翻转

    [python图像处理]图像的缩放.旋转与翻转 图像的几何变换,如缩放.旋转和翻转等,在图像处理中扮演着重要的角色,python中的Image类分别提供了这些操作的接口函数,下面进行逐一介绍. 1.图 ...

  3. Linux MMC framework2:基本组件之host

    声明:本文很多内容和思路参考了http://www.wowotech.net/comm/mmc_host_driver.html,对原作者表示感谢! 1.前言 本文是Linux MMC framewo ...

  4. nodejs 数据库操作,消息的发送和接收,模拟同步

    var deasync = require('deasync'); //导入模板 var mysql=require('mysql'); var Stomp = require('stompjs'); ...

  5. genstr.py

    #!/usr/bin/python #-*- coding:utf-8 –*- import os import sys import re import shutil import xlrd imp ...

  6. 出现“error LNK1169: 找到一个或多个多重定义的符号”的原因

    或许,有人真的会这样写程序吧...所以才会碰到如下哥们提出的问题. https://zhidao.baidu.com/question/131426210.html 出现这种问题的原因链接中的最佳答案 ...

  7. php-fpm 配置文件检测

    用过 Nginx 的兄弟都知道,修改 Nginx 配置文件之后,可以使用 nginx -t 来检测配置文件是否有语法错误. 今天配置 opcache 的时候,发现 php-fpm 也可以检测 php- ...

  8. python易错题之lambda 以及 for循环中内嵌函数

    li = [] for x in range(10): print(x) //在函数没有执行前(li[0]()),for 循环中x已经执行完,x会一直为 9 def fun(): print(x) / ...

  9. pytest七:assert断言

    断言是写自动化测试基本最重要的一步,一个用例没有断言,就失去了自动化测试的意义了.什么是断言呢?简单来讲就是实际结果和期望结果去对比,符合预期那就测试 pass,不符合预期那就测试 failed py ...

  10. 常见的MySQL Replication Error

    现在不少公司都在用MySQL(master)-->MySQL(slave)的框架,当然也有一主多从的架构,这也是MySQL主从的一个延伸架构;当然也有的公司MySQL主主的架构,MySQL主主架 ...