卡了一晚上,经历了被卡空间,被卡T,被卡数组等一堆惨惨的事情之后,终于在各位大爹的帮助下过了这个题qwqqq

(全网都没有用矩阵转移的动态dp,让我很慌张)

首先,我们先考虑一个比较基础的\(dp\)

我们令\(dp1[i]\)表示必须选\(i\)的最大连通块的权值是多少。

然后令\(ans1[i]\)表示\(i\)的子树中的\(dp1\)的最大值。

那么该怎么计算这两个数组呢。

首先$$dp1[i] = dp1[i]+max(0,dp1[p])$$ 其中\(p\)是\(i\)的儿子。

\[ans1[i]=max(ans1[i],ans1[p]),ans1[i]=max(ans1[i],dp1[i])
\]

那么朴素的\(dp\)大概就是这样。

应该如何去优化呢。

由于是树上问题,不难想到树链剖分然后划分出轻重儿子。

我们令\(g\)表示考虑重儿子的\(dp\)数组,\(ans\)表示考虑重儿子的\(max\)数组。

然后\(f,ans1\)分别表示不考虑重儿子的\(dp和max\)数组。

若\(p\)是\(x\)的重儿子的话

不难发现,\(g[x]=max(f[x],g[p]+f[x]),ans[x]=max(ans1[x],max(ans[p],g[p]+f[x]))\)

我们这里默认\(ans1[x]\)一开始和\(f[x]\)取过\(max\).

那么我们就可以直接构造如下矩阵

f[x] f[x] -inf
-inf 0 -inf
f[x] ans1[x] 0

来进行转移(注意特判链尾)

但是有一个需要注意的地方,就是我们的\(ans1[x]\)由于涉及到修改,所以需要一个可删堆(或者\(multiset\))

来进行维护,感觉\(modify\)的时候还是有很多细节。qwq懒得写太多了,直接看代码吧。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<map>
#include<set>
#define pb push_back
#define mk make_pair
#define ll long long
#define lson ch[x][0]
#define rson ch[x][1]
#define rint register int
using namespace std;
//char buf[100010],*buff = buf + 100000; //#define getchar() (buff == buf + 100000 ? (fread(buf,1,100000,stdin),buff = buf) : 0,*buff++) inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
} const int maxn = 2e5+1e2;
const int maxm = 2*maxn;
const ll inf = -1e15; struct Node{
priority_queue<ll> q1,q2;
inline void push(ll x) {q1.push(x);}
inline void erase(ll x) {q2.push(x);}
inline ll top(){while ((!q2.empty()) && (q1.top()==q2.top())) q1.pop(),q2.pop(); return q1.top();}
}; Node ans[maxn]; struct Ju{
int x,y;
ll a[3][3];
Ju operator * (Ju b)
{
Ju ans;
ans.x=x;
ans.y=b.y;
for (rint i=0;i<=ans.x;++i)
{
for (rint j=0;j<=ans.y;++j)
{
ll uu = inf;
for (rint k=0;k<=y;++k)
uu=max(uu,a[i][k]+b.a[k][j]);
ans.a[i][j]=uu;
}
}
return ans;
}
}; int point[maxn],nxt[maxm],to[maxm];
int cnt,n,m;
int top[maxn],newnum[maxn],fa[maxn],son[maxn],size[maxn];
Ju f[4*maxn];
//Ju pre[maxn];
ll dp1[maxn],dp[maxn];
ll ans1[maxn];
int back[maxn];
int tot;
int tail[maxn];
ll val[maxn];
int leaf[maxn];
Ju caonima; struct pp{
ll cao,kao;
}; pp cnm[maxn]; inline void addedge(int x,int y)
{
nxt[++cnt]=point[x];
to[cnt]=y;
point[x]=cnt;
} inline void up(int root)
{
f[root]=f[2*root+1]*f[2*root];
} void build(int root,int l,int r)
{
if (l==r)
{
int ymh = back[l]; if (tail[top[ymh]]==ymh)
{
f[root].x=0;
f[root].y=2;
f[root].a[0][0]=dp[ymh];
f[root].a[0][1]=max(dp[ymh],0ll);
}
else
{ f[root].x=f[root].y=2;
f[root].a[0][0]=dp[ymh];
f[root].a[0][1]=dp[ymh];
f[root].a[0][2]=inf;
f[root].a[1][0]=inf;
f[root].a[1][2]=inf;
f[root].a[2][0]=dp[ymh];
f[root].a[2][1]=ans[ymh].top();
}
leaf[ymh]=root;
return;
}
int mid = l+r >> 1;
build(root<<1,l,mid);
build(root<<1|1,mid+1,r);
up(root);
} void update(int root,int l,int r,int x)
{
if (l==r)
{
f[root]=caonima;
return;
}
int mid = l+r >> 1;
if (x<=mid) update(root<<1,l,mid,x);
else update(root<<1|1,mid+1,r,x);
up(root);
} Ju query(int root,int l,int r,int x,int y)
{
if(x<=l && r<=y)
{
return f[root];
}
int mid = l+r >> 1;
if (y<=mid) return query(root<<1,l,mid,x,y);
if (x>mid) return query(root<<1|1,mid+1,r,x,y);
return query(root<<1|1,mid+1,r,x,y)*query(root<<1,l,mid,x,y);
} void dfs1(int x,int faa)
{
size[x]=1;
int maxson=-1;
for (int i=point[x];i;i=nxt[i])
{
int p = to[i];
if (p==faa) continue;
fa[p]=x;
dfs1(p,x);
size[x]+=size[p];
if (size[p]>maxson)
{
maxson=size[p];
son[x]=p;
}
}
} void dfs2(int x,int chain)
{
top[x]=chain;
tail[chain]=x;
newnum[x]=++tot;
back[tot]=x;
if (!son[x]) return;
dfs2(son[x],chain);
for (int i=point[x];i;i=nxt[i])
{
int p = to[i];
if (!newnum[p]) dfs2(p,p);
}
} void solve(int x,int fa)
{
dp[x]=dp1[x]=val[x];
ans1[x]=val[x];
for (rint i=point[x];i;i=nxt[i])
{
int p = to[i];
if (p==fa) continue;
solve(p,x);
dp1[x]=max(dp1[x],dp1[x]+dp1[p]);
ans1[x]=max(ans1[x],ans1[p]);
if (p!=son[x]) dp[x]=max(dp[x],dp[x]+dp1[p]),ans[x].push(ans1[p]);
}
ans[x].push(dp[x]);
ans[x].push(0);
//cout<<x<<endl;
ans1[x]=max(ans1[x],dp1[x]);
ans1[x]=max(ans1[x],0ll);
//cout<<x<<endl;
} void modify(int x,ll y)
{
Ju tmp = f[leaf[x]];
if (tail[top[x]]==x)
{
tmp.a[0][0]+=y-val[x];
tmp.a[0][1]=max(0ll,tmp.a[0][0]);
}
else
{
ans[x].erase(tmp.a[0][0]);
tmp.a[0][0]+=y-val[x];
tmp.a[0][1]=tmp.a[0][0];
ans[x].push(tmp.a[0][0]);
tmp.a[2][0]=tmp.a[0][0];
tmp.a[2][1]=ans[x].top();
}
val[x]=y;
caonima=tmp;
update(1,1,n,newnum[x]);
//cout<<x<<" "<<top[x]<<endl;
for (rint now = top[x];now!=1;now=top[now])
{
//cout<<now<<endl;
int faa = fa[now];
//Ju ymh = query(1,1,n,newnum[faa],newnum[faa]);
Ju ymh = f[leaf[faa]];
Ju lyf;
if (newnum[now]!=newnum[tail[top[now]]])
lyf= query(1,1,n,newnum[now],newnum[tail[top[now]]]);
else
lyf = f[leaf[now]];
ans[faa].erase(cnm[now].kao);
ans[faa].erase(ymh.a[0][0]);
ymh.a[0][0]+=max(lyf.a[0][0],0ll)-max(cnm[now].cao,0ll);
ymh.a[0][1]=ymh.a[0][0];
ymh.a[2][0]=ymh.a[0][0];
ans[faa].push(ymh.a[0][0]);
ans[faa].push(lyf.a[0][1]);
ymh.a[2][1]=ans[faa].top();
caonima=ymh;
update(1,1,n,newnum[faa]);
cnm[now].kao=lyf.a[0][1];
cnm[now].cao=lyf.a[0][0];
now=fa[now];
}
} int q;
char c; signed main()
{
//freopen("5210.in","r",stdin);
//freopen("ymh.out","w",stdout);
n=read(),q=read();
//cout<<n<<" "<<q<<endl;
for (rint i=1;i<=n;i++) val[i]=read();
for (rint i=1;i<n;i++)
{
int x=read(),y=read();
addedge(x,y);
addedge(y,x);
}
dfs1(1,0);
dfs2(1,1);
solve(1,0);
//cout<<1<<endl;
build(1,1,n);
//cout<<ans1[2]<<" "<<dp[2]<<" "<<dp1[2]<<endl;
for (rint i=1;i<=n;++i)
{
Ju ymh = query(1,1,n,newnum[i],newnum[tail[top[i]]]);
cnm[i].cao=ymh.a[0][0];
cnm[i].kao=ymh.a[0][1];
}
for (rint i=1;i<=q;++i)
{
c=getchar();
while (c<'A' || c>'Z') c=getchar();
if (c=='M')
{
ll x=read(),y=read();
modify(x,y);
}
else
{
int x=read();
Ju ymh = query(1,1,n,newnum[x],newnum[tail[top[x]]]);
cout<<max(ymh.a[0][1],max(ymh.a[0][0],0ll))<<"\n";
}
}
return 0;
}

bzoj5210最大连通子块和 (动态dp+卡常好题)的更多相关文章

  1. bzoj5210 最大连通子块和 动态 DP + 堆

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=5210 题解 令 \(dp[x][0]\) 表示以 \(x\) 为根的子树中的包含 \(x\) ...

  2. 5210: 最大连通子块和 动态DP 树链剖分

    国际惯例的题面:这题......最大连通子块和显然可以DP,加上修改显然就是动态DP了......考虑正常情况下怎么DP:我们令a[i]表示选择i及i的子树中的一些点,最大连通子块和;b[i]表示在i ...

  3. 【bzoj5210】最大连通子块和 动态dp

    动态$dp$好题 考虑用树链剖分将整棵树剖成若干条链. 设x的重儿子为$son[x]$,设$x$所在链链头为$top[x]$ 对于重链上的每个节点(不妨设该节点编号为$x$)令$f[x]$表示以$x$ ...

  4. bzoj 5210 最大连通子块和——动态DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=5210 似乎像bzoj4712一样,依然可以用别的方法做.但还是只写了动态DP. 当然是dp[ ...

  5. 2019.02.15 bzoj5210: 最大连通子块和(链分治+ddp)

    传送门 题意:支持单点修改,维护子树里的最大连通子块和. 思路: 扯皮: bzojbzojbzoj卡常差评. 网上的题解大多用了跟什么最大子段和一样的转移方法. 但是我们实际上是可以用矩阵转移的传统d ...

  6. BZOJ5210 最大连通子块和 【树链剖分】【堆】【动态DP】

    题目分析: 解决了上次提到的<切树游戏>后,这道题就是一道模板题. 注意我们需要用堆维护子重链的最大值.这样不会使得复杂度变坏,因为每个重链我们只考虑一个点. 时间复杂度$O(nlog^2 ...

  7. bzoj5210最大连通子块和

    题解: 考虑朴素的dp:$$f_{u} = max(\sum_{v} f_{v} + w_{u} , 0)  \ \ \ \ h_{u} = max( max_{v} \{ h_{v} \}  , h ...

  8. 【BZOJ5210】最大连通子块和 树剖线段树+动态DP

    [BZOJ5210]最大连通子块和 Description 给出一棵n个点.以1为根的有根树,点有点权.要求支持如下两种操作: M x y:将点x的点权改为y: Q x:求以x为根的子树的最大连通子块 ...

  9. 【bzoj5210】最大连通子块和 树链剖分+线段树+可删除堆维护树形动态dp

    题目描述 给出一棵n个点.以1为根的有根树,点有点权.要求支持如下两种操作: M x y:将点x的点权改为y: Q x:求以x为根的子树的最大连通子块和. 其中,一棵子树的最大连通子块和指的是:该子树 ...

随机推荐

  1. Ubuntu16.04 Linux 下安装、配置SSH

    本人在Win7+VMware下利用两个ubuntu虚拟机安装.配置.测试了SSH. 在Server端安装openssh-server. sudo apt-get install ssh # 安装ssh ...

  2. Go-内置函数之append、recover用法

    package main import "fmt" import "time" func test() { defer func() { if err := r ...

  3. 【开发工具】Postman保姆级入门教程

    目录 一.简单使用 1. 创建命名空间 2. 创建新集合 3. 按模块整理接口 二.使用环境变量 1. 创建环境与环境变量 2. 使用环境变量 3. 登录后自动更新环境变量 转载请注明出处 一.简单使 ...

  4. Django实现基本的页面分页

    1.视图views.py from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage def index(requ ...

  5. Python - //和/的区别

    / 表示浮点数除法,返回浮点结果; // 表示整数除法,返回不大于结果的一个最大的整数 print("6 // 4 = " + str(6 // 4)) print("6 ...

  6. Linux上项目部署在home目录中无法访问的问题

    在Linux上开发一个Web项目,使用nginx作为Web服务器.在nginx的配置文件中添加一个server,root路径写的是放在home目录中的项目目录的路径.打开浏览器访问,提示错误:403 ...

  7. Spring5(六)——AspectJ(xml)

    一.AspectJ 1.介绍 AspectJ是一个面向切面的框架,它扩展了Java语言.AspectJ定义了AOP语法,也可以说 AspectJ 是一个基于 Java 语言的 AOP 框架.通常我们在 ...

  8. Markdown快捷键详解(源自狂神说Java视频,整理版)

    #加空格 表示是一个标题(一级标题)回车后直接生成 例下 Markdown学习 标题: ##(两个#号)空格加标题名字即为二级标题例下 二级标题 三个#号 加标题名字即为三级标题,与二级标题等有层级关 ...

  9. ldconfig与 /etc/ld.so.conf

    现在我们知道了动态与静态函数库,也知道了当前的Linux大多是将函数库做成动态函数库,下面来讨论增加函数库读取性能的方法.我们知道,内存的访问速度是硬盘的好几倍,所以,如果将常用的动态函数库加载到内存 ...

  10. promise入门基本使用

    Promise入门详解和基本用法   异步调用 异步 JavaScript的执行环境是单线程. 所谓单线程,是指JS引擎中负责解释和执行JavaScript代码的线程只有一个,也就是一次只能完成一项任 ...