https://www.luogu.org/problemnew/show/P4643

很妙……让我重新又看了一遍猫锟的WC课件。

推荐一个有markdown神犇题解:https://www.cnblogs.com/RabbitHu/p/9112811.html

本文的代码和就是在此基础上改动与细化(更符合我这种蒟蒻的阅读体验)

————————————————————————

这道题是课件的模板题。

首先需要明白这个最大独立集是指取了u结点则不能取与u相连的点v。

不带修改的话能够看出这就是“没有上司的舞会”,于是先把静态的dp敲出来。

f[i][0/1]为节点i当i不取/取的时候其子树产生的最大价值。

方程f[u][0]=sigma(max(f[v][0],f[v][1]))

f[u][1]=w[u]+sigma(f[v][0])

接下来让它“动”起来,按照一般套路修改应当在线段树上做,于是先码一个树链剖分再说。

我们发现:重链的信息好储存,但是重链的侧链(轻链)没有办法只靠f就能够将信息合并到轻链上。

于是思考可以再开一个数组来压缩一些信息使其能够放到重链上。

g[i][0/1]表示节点i当i不取/取时,i不在这条链上的子孙的答案(即最大独立集)。

不难用g来更新f数组。

f[u][0]=g[u][0]+max(f[v][0],f[v][1])

f[u][1]=g[u][1]+f[v][0]

(u,v在一条重链上,且fa[v]=u)

为了去除冗杂,我们采用矩阵的方法来表示这个式子。

g[i][0],g[i][0]    (运算->)f[v][0]  (等于) f[u][0]

g[i][1],   0                         f[v][1]                 f[u][1]

运算定义如下(就直接拿代码来说了,反正您们看得懂):

matrix operator *(const matrix &b)const{
matrix c;
for(int i=;i<;i++)
for(int j=;j<;j++)
for(int k=;k<;k++)
c.g[i][j]=max(c.g[i][j],g[i][k]+b.g[k][j]);
return c;
}

用线段树维护矩阵,则1所在的重链的所有节点的矩阵运算在一起即为1结点不取/取的答案。

可能你会有疑问,我们只维护了g数组,怎么就得出了f数组的功能呢?

别忘了链的底端u是没有v的啊!所以我们只用g数组往前推就行了啊。

那么修改u,就需要将u到1的路径上所有的重链的信息全部修改一遍。

为了优化时间,不至于每次修改都要重新搜一遍该点所连接的所有非链上的点(TLE警告),我们开一个val矩阵,其功能可以理解为线段树上的lazy。初始时val就等于对应结点的矩阵。

实际上就是修改一条重链i,对于它的父亲重链j的最后一个结点要根据i所得到的f值来更新这个结点的g矩阵。

细节讲起来也是很麻烦的,直接看代码吧(反正您们看得懂)。

void path_modify(int u,int c){
val[pos[u]].g[][]+=c-w[u];w[u]=c;
while(u){
matrix od=query(,,n,pos[top[u]],pos[ed[u]]);
modify(,,n,pos[u]);//将会用val矩阵替换掉对应位置的矩阵
matrix nw=query(,,n,pos[top[u]],pos[ed[u]]);
u=fa[top[u]];
val[pos[u]].g[][]+=max(nw.g[][],nw.g[][])-max(od.g[][],od.g[][]);
val[pos[u]].g[][]=val[pos[u]].g[][];
val[pos[u]].g[][]+=nw.g[][]-od.g[][];
}
}

于是我们成功地AC了这道题(但愿这种题永远不要出出来。)

#include<cmath>
#include<queue>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1e5+;
inline int read(){
int X=,w=;char ch=;
while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
while(isdigit(ch))X=(X<<)+(X<<)+(ch^),ch=getchar();
return w?-X:X;
}
struct matrix{
ll g[][];
matrix(){
memset(g,,sizeof(g));
}
matrix operator *(const matrix &b)const{
matrix c;
for(int i=;i<;i++)
for(int j=;j<;j++)
for(int k=;k<;k++)
c.g[i][j]=max(c.g[i][j],g[i][k]+b.g[k][j]);
return c;
}
}val[N],tr[N*];
struct node{
int to,nxt;
}e[N*];
ll w[N],f[N][];
int n,m,cnt,tot,head[N];
int dep[N],fa[N],size[N],son[N],top[N],pos[N],idx[N],ed[N];
inline void add(int u,int v){
e[++cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt;
}
void dfs1(int u){
int sum=;size[u]=;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa[u])continue;
fa[v]=u;dfs1(v);
size[u]+=size[v];
if(!son[u]||size[son[u]]<size[v])son[u]=v;
f[u][]+=max(f[v][],f[v][]);
sum+=f[v][];
}
f[u][]=sum+w[u];
}
void dfs2(int u,int anc){
pos[u]=++tot;idx[tot]=u;top[u]=anc;
if(!son[u]){ed[u]=u;return;}
dfs2(son[u],anc);ed[u]=ed[son[u]];
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa[u]||v==son[u])continue;
dfs2(v,v);
}
}
void init(){
dep[]=;
dfs1();
dfs2(,);
}
void build(int a,int l,int r){
if(l==r){
int u=idx[l];
ll g0=,g1=w[u];
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa[u]||v==son[u])continue;
g0+=max(f[v][],f[v][]);g1+=f[v][];
}
tr[a].g[][]=tr[a].g[][]=g0;
tr[a].g[][]=g1;
val[l]=tr[a];
return;
}
int mid=(l+r)>>;
build(a<<,l,mid);build(a<<|,mid+,r);
tr[a]=tr[a<<]*tr[a<<|];
}
matrix query(int a,int l,int r,int l1,int r1){
if(l1<=l&&r<=r1)return tr[a];
int mid=(l+r)>>;
if(r1<=mid)return query(a<<,l,mid,l1,r1);
if(l1>mid)return query(a<<|,mid+,r,l1,r1);
return query(a<<,l,mid,l1,mid)*query(a<<|,mid+,r,mid+,r1);
}
void modify(int a,int l,int r,int k){
if(l==r){
tr[a]=val[l];
return;
}
int mid=(l+r)>>;
if(k<=mid)modify(a<<,l,mid,k);
else modify(a<<|,mid+,r,k);
tr[a]=tr[a<<]*tr[a<<|];
}
void path_modify(int u,int c){
val[pos[u]].g[][]+=c-w[u];w[u]=c;
while(u){
matrix od=query(,,n,pos[top[u]],pos[ed[u]]);
modify(,,n,pos[u]);
matrix nw=query(,,n,pos[top[u]],pos[ed[u]]);
u=fa[top[u]];
val[pos[u]].g[][]+=max(nw.g[][],nw.g[][])-max(od.g[][],od.g[][]);
val[pos[u]].g[][]=val[pos[u]].g[][];
val[pos[u]].g[][]+=nw.g[][]-od.g[][];
}
}
int main(){
n=read(),m=read();
for(int i=;i<=n;i++)w[i]=read();
for(int i=;i<n;i++){
int u=read(),v=read();
add(u,v);add(v,u);
}
init();
build(,,n);
for(int i=;i<=m;i++){
int u=read(),x=read();
path_modify(u,x);
matrix ans=query(,,n,pos[top[]],pos[ed[]]);
printf("%lld\n",max(ans.g[][],ans.g[][]));
}
return ;
}

洛谷4643:【模板】动态dp——题解的更多相关文章

  1. 【洛谷P4719】动态dp 动态dp模板

    题目大意:给你一颗$n$个点的树,点有点权,有$m$次操作,每次操作给定$x$,$y$,表示修改点$x$的权值为$y$. 你需要在每次操作之后求出这棵树的最大权独立集的权值大小. 数据范围:$n,m≤ ...

  2. 【洛谷4719】 动态dp(树链剖分,dp,矩阵乘法)

    前言 其实我只是为了过掉模板而写的ddp,实际应用被吊着锤 Solution 并不想写详细的过程 一句话过程:将子树中轻儿子的贡献挂到这个点上面来 详细版:(引用yyb) 总结一下的话,大致的过程是这 ...

  3. 洛谷P1783 海滩防御 分析+题解代码

    洛谷P1783 海滩防御 分析+题解代码 题目描述: WLP同学最近迷上了一款网络联机对战游戏(终于知道为毛JOHNKRAM每天刷洛谷效率那么低了),但是他却为了这个游戏很苦恼,因为他在海边的造船厂和 ...

  4. 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)

    To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...

  5. 洛谷P4047 [JSOI2010]部落划分题解

    洛谷P4047 [JSOI2010]部落划分题解 题目描述 聪聪研究发现,荒岛野人总是过着群居的生活,但是,并不是整个荒岛上的所有野人都属于同一个部落,野人们总是拉帮结派形成属于自己的部落,不同的部落 ...

  6. 洛谷P1155 双栈排序题解(图论模型转换+二分图染色+栈)

    洛谷P1155 双栈排序题解(图论模型转换+二分图染色+栈) 标签:题解 阅读体验:https://zybuluo.com/Junlier/note/1311990 原题地址:洛谷P1155 双栈排序 ...

  7. LCT总结——概念篇+洛谷P3690[模板]Link Cut Tree(动态树)(LCT,Splay)

    为了优化体验(其实是强迫症),蒟蒻把总结拆成了两篇,方便不同学习阶段的Dalao们切换. LCT总结--应用篇戳这里 概念.性质简述 首先介绍一下链剖分的概念(感谢laofu的讲课) 链剖分,是指一类 ...

  8. 洛谷1387 二维dp 不是特别简略的题解 智商题

    洛谷1387 dp题目,刚开始写的时候使用了前缀和加搜索,复杂度大概在O(n ^ 3)级别,感觉这么写还是比较对得起普及/提高-的难度的..后来看了题解区各位大神的题解,开始一脸mb,之后备受启发. ...

  9. 洛谷10月月赛II题解

    [咻咻咻] (https://www.luogu.org/contestnew/show/11616) 令人窒息的洛谷月赛,即将参加NOIp的我竟然只会一道题(也可以说一道也不会),最终145的我只能 ...

  10. NOIP2017提高组Day2T2 宝藏 洛谷P3959 状压dp

    原文链接https://www.cnblogs.com/zhouzhendong/p/9261079.html 题目传送门 - 洛谷P3959 题目传送门 - Vijos P2032 题意 给定一个 ...

随机推荐

  1. CentOS 7.2-编译安装zabbix 3.4

    起因: 前面已经使用yum安装了zabbix 3.4了,准备去交差了,交差时老大明确要求必须使用编译安装,统一放在/usr/local目录下.... 重来吧!! 一.环境说明 本次安装使用CentOS ...

  2. lesson 19 A very dear cat

    lesson 19 A very dear cat dear adj. 亲爱的:尊敬的:昂贵的 表示几乎不,很少的词语 rarely hardly seldom scarcely flat = apa ...

  3. python中为什么 if/while/def/class语句需要冒号?

    python中冒号主要用于增强可读性(ABC语言实验的结果之一).考虑一下这个: if a == b print(a) 与 if a == b: print(a) 注意第二种方法稍微容易一些.请进一步 ...

  4. 集合栈计算机 (The SetStack Computer,ACM/ICPC NWERC 2006,UVa12096

    题目描述: #include<iostream> #include<string> #include<set> #include<map> #inclu ...

  5. Elasticsearch 评分score计算中的Boost 和 queryNorm

    本来没有这篇文章,在公司分享ES的时候遇到一个问题,使用boost的时候,怎么从评分score中知道boost的影响. 虽然我们从查询结果可以直观看到,boost起了应有的作用,但是在explain的 ...

  6. Python3 数据类型-字符串

    字符串是 Python 中最常用的数据类型,是一个个字符组成的有序的序列,是字符的集合. 一 字符串定义 创建字符串很简单,可以使用引号('或"或""")来创建 ...

  7. python函数中的位置参数、默认参数、关键字参数、可变参数区别

    一.位置参数 调用函数时根据函数定义的参数位置来传递参数. #!/usr/bin/env python # coding=utf-8 def print_hello(name, sex): sex_d ...

  8. Python中变量名里面的下划线

    1 变量名前后都有两个下划线(__X__),表示是系统级变量: 2 变量名前只有一个下划线(_X),表示该变量不是由from module import *导入进来的: 3 变量名前有两个下划线(__ ...

  9. Python—字典(当索引不好用时)

    一.定义与概念 1.字典是针对非序列集合而提供的一种数据类型 举例:检索学生信息. “<键><值>对”. 键(即身份证号码) 值(即学生信息). “键值对”例子 姓名和电话号码 ...

  10. “Hello world!”团队第一周贡献分分配结果

    小组名称:Hello World! 项目名称:空天猎 组长:陈建宇 成员:刘成志.阚博文.刘淑霞.黄泽宇.方铭.贾男男 第一周贡献分分配结果   基础分 会议分 提功能分 个人表现分 各项总分 最终分 ...