UOJ268 [清华集训2016] 数据交互 【动态DP】【堆】【树链剖分】【线段树】
题目分析:
不难发现可以用动态DP做。
题目相当于是要我求一条路径,所有与路径有交的链的代价加入进去,要求代价最大。
我们把链的代价分成两个部分:一部分将代价加入$LCA$之中,用$g$数组保存;另一部分将代价加在整条链上,用$d$数组保存。
这时候我们可以发现,一条从$u$到$v$的路径的代价相当于是$d[LCA(u,v)]+\sum_{x \in edge(u,v)}g[x]$。
如果是静态的,可以用树形DP解决。
看过《神奇的子图》的同学都知道,叶子结点是从它的儿子中取两个最大的出来,所以堆维护。
考虑合并。
链从左延申出的最大的$g$的总和记录。链从右延申包括$d$的总和记录,每次向上$update$的时候拼起来与原答案比较即可。
代码:
#include<bits/stdc++.h>
using namespace std; typedef long long ll; const int maxn = ; int n,m; vector <int> g[maxn];
int sz[maxn],top[maxn],fa[maxn],dep[maxn],son[maxn],ind[maxn],dr[maxn];
int tail[maxn],num; struct Query{int from,to,w;}Q[maxn];
struct Priority_Queue{
priority_queue<ll,vector<ll>,less<ll> > pq,del;
void push(ll now){pq.push(now);}
void pop(){
while(!del.empty() && pq.top() == del.top()) pq.pop(),del.pop();
pq.pop();
}
ll top(){
while(!del.empty() && pq.top() == del.top()) pq.pop(),del.pop();
if(pq.empty()) return ;
else return pq.top();
}
ll sec(){
while(!del.empty() && pq.top() == del.top()) pq.pop(),del.pop();
if(pq.size() ==) return ;
ll oop = pq.top(); pq.pop();
while(!del.empty() && pq.top() == del.top()) pq.pop(),del.pop();
if(pq.size() == ){pq.push(oop);return ;}
else {ll ret = pq.top();pq.push(oop);return ret;}
}
void Erase(ll now){del.push(now);}
}Son[maxn],Ans; struct segmentTree{
ll tg,ff,lazy,REC,L,R;
}T[maxn<<]; void push_down(int now){
T[now<<].ff += T[now].lazy; T[now<<|].ff += T[now].lazy;
T[now<<].REC += T[now].lazy; T[now<<|].REC += T[now].lazy;
T[now<<].lazy += T[now].lazy; T[now<<|].lazy += T[now].lazy;
T[now<<].R += T[now].lazy; T[now<<|].R += T[now].lazy;
T[now].lazy = ;
} segmentTree merge(segmentTree alpha,segmentTree beta){
segmentTree RES;RES.lazy = ; RES.ff = ;
RES.tg = alpha.tg + beta.tg;
RES.REC = max(alpha.REC,beta.REC);
RES.REC = max(RES.REC,alpha.R + beta.L);
RES.L = max(alpha.L,alpha.tg + beta.L);
RES.R = max(beta.R,alpha.R + beta.tg);
return RES;
} void dfs1(int now,int f,int dp){
dep[now] = dp; fa[now] = f;
int maxx = ;
for(auto it:g[now]){
if(it == f) continue;
dfs1(it,now,dp+);
sz[now] += sz[it];
if(maxx == || sz[it] > sz[maxx]) maxx = it;
}
son[now] = maxx; sz[now]++;
} void dfs2(int now,int tp){
top[now] = tp; ind[now] = ++num; dr[num] = now;
if(now == tp) Ans.push();
if(son[now]) dfs2(son[now],tp);
else tail[tp] = now;
for(auto it : g[now]){
if(it == fa[now] || it == son[now]) continue;
dfs2(it,it);
}
} void read(){
scanf("%d%d",&n,&m);
for(int i=;i<n;i++){
int u,v; scanf("%d%d",&u,&v);
g[u].push_back(v); g[v].push_back(u);
}
} int QueryLca(int u,int v){
while(top[u] != top[v]){
if(dep[top[u]] > dep[top[v]]) u = fa[top[u]];
else v = fa[top[v]];
}
if(dep[u] < dep[v]) return u; else return v;
} segmentTree Querylen(int now,int tl,int tr,int l,int r){
if(tl >= l && tr <= r) return T[now];
if(T[now].lazy) push_down(now);
int mid = (tl+tr)/;
if(mid < l) return Querylen(now<<|,mid+,tr,l,r);
if(mid >= r) return Querylen(now<<,tl,mid,l,r);
segmentTree pp = Querylen(now<<|,mid+,tr,l,r);
segmentTree qq = Querylen(now<<,tl,mid,l,r);
return merge(qq,pp);
} void AddG(int now,int tl,int tr,int place,int w){
if(tl == tr){T[now].tg += w;T[now].L += w;return;}
if(T[now].lazy) push_down(now);
int mid = (tl+tr)/;
if(mid >= place) AddG(now<<,tl,mid,place,w);
else AddG(now<<|,mid+,tr,place,w);
T[now] = merge(T[now<<],T[now<<|]);
} void ModifyG(int now,int tl,int tr,int place){
if(tl == tr){
tl = dr[tl]; T[now].L = Son[tl].top() + T[now].tg;
T[now].REC = T[now].ff + Son[tl].top() + Son[tl].sec();
T[now].R = T[now].ff + Son[tl].top();
return;
}
if(T[now].lazy) push_down(now);
int mid = (tl+tr)/;
if(mid >= place) ModifyG(now<<,tl,mid,place);
else ModifyG(now<<|,mid+,tr,place);
T[now] = merge(T[now<<],T[now<<|]);
} void ModifyF(int now,int tl,int tr,int l,int r,int w){
if(tl >= l && tr <= r){
T[now].lazy += w; T[now].ff += w; T[now].R += w; T[now].REC+=w;
return;
}
if(T[now].lazy) push_down(now);
int mid = (tl+tr)/;
if(mid >= l) ModifyF(now<<,tl,mid,l,r,w);
if(mid+ <= r) ModifyF(now<<|,mid+,tr,l,r,w);
T[now] = merge(T[now<<],T[now<<|]);
} void SingleModify(int now,int w){
int hole = tail[top[now]];
segmentTree fk = Querylen(,,n,ind[top[hole]],ind[hole]);
AddG(,,n,ind[now],w); now=fa[top[now]];
while(now){
segmentTree rl = Querylen(,,n,ind[top[hole]],ind[hole]);
Ans.Erase(fk.REC); Ans.push(rl.REC);
Son[now].Erase(fk.L); Son[now].push(rl.L);
hole = tail[top[now]]; fk = Querylen(,,n,ind[top[hole]],ind[hole]);
ModifyG(,,n,ind[now]); now = fa[top[now]];
}
Ans.Erase(fk.REC);
fk = Querylen(,,n,ind[top[hole]],ind[hole]);
Ans.push(fk.REC);
} void WideModify(int now,int LCA,int w){
while(dep[now] >= dep[LCA]){
segmentTree fk = Querylen(,,n,ind[top[now]],ind[tail[top[now]]]);
if(dep[top[now]] < dep[LCA]) ModifyF(,,n,ind[LCA],ind[now],w);
else ModifyF(,,n,ind[top[now]],ind[now],w);
Ans.Erase(fk.REC);
fk = Querylen(,,n,ind[top[now]],ind[tail[top[now]]]);
Ans.push(fk.REC);
now = fa[top[now]];
}
} void Modify(int u,int v,int w){
int LCA = QueryLca(u,v);
SingleModify(LCA,w);
WideModify(u,LCA,w); // u
WideModify(v,LCA,w); // v
WideModify(LCA,LCA,-w); // LCA
} void build_tree(int now,int tl,int tr){
if(tl == tr){
tl = dr[tl];
for(auto it : g[tl]){
if(it == son[tl] || it == fa[tl]) continue;
Son[tl].push();
}
}else{
int mid = (tl+tr)/;
build_tree(now<<,tl,mid); build_tree(now<<|,mid+,tr);
}
} void work(){
dfs1(,,);
dfs2(,);
build_tree(,,n);
for(int i=;i<=m;i++){
char ch = getchar(); while(ch != '+' && ch != '-') ch = getchar();
int fr,t,w;
if(ch == '+'){
scanf("%d%d%d",&fr,&t,&w);Q[i].from=fr;Q[i].to=t;Q[i].w=w;
}else{
int x; scanf("%d",&x);fr = Q[x].from,t = Q[x].to,w = -Q[x].w;
}
Modify(fr,t,w);
printf("%lld\n",Ans.top());
}
} int main(){
read();
work();
return ;
}
UOJ268 [清华集训2016] 数据交互 【动态DP】【堆】【树链剖分】【线段树】的更多相关文章
- 【bzoj5210】最大连通子块和 树链剖分+线段树+可删除堆维护树形动态dp
题目描述 给出一棵n个点.以1为根的有根树,点有点权.要求支持如下两种操作: M x y:将点x的点权改为y: Q x:求以x为根的子树的最大连通子块和. 其中,一棵子树的最大连通子块和指的是:该子树 ...
- 【bzoj4712】洪水 树链剖分+线段树维护树形动态dp
题目描述 给出一棵树,点有点权.多次增加某个点的点权,并在某一棵子树中询问:选出若干个节点,使得每个叶子节点到根节点的路径上至少有一个节点被选择,求选出的点的点权和的最小值. 输入 输入文件第一行包含 ...
- 洛谷P3313 [SDOI2014]旅行 题解 树链剖分+线段树动态开点
题目链接:https://www.luogu.org/problem/P3313 这道题目就是树链剖分+线段树动态开点. 然后做这道题目之前我们先来看一道不考虑树链剖分之后完全相同的线段树动态开点的题 ...
- P3313 [SDOI2014]旅行——树链剖分+线段树(动态开点?)
P3313 [SDOI2014]旅行 一棵树,其中的点分类,点有权值,在一条链上找到一类点中的最大值或总和: 树链剖分把树变成链: 把每个宗教单开一个线段树,维护区间总和和最大值: 宗教很多,需要动态 ...
- BZOJ4712洪水——动态DP+树链剖分+线段树
题目描述 小A走到一个山脚下,准备给自己造一个小屋.这时候,小A的朋友(op,又叫管理员)打开了创造模式,然后飞到 山顶放了格水.于是小A面前出现了一个瀑布.作为平民的小A只好老实巴交地爬山堵水.那么 ...
- Tsinsen A1517. 动态树 树链剖分,线段树,子树操作
题目 : http://www.tsinsen.com/A1517 A1517. 动态树 时间限制:3.0s 内存限制:1.0GB 总提交次数:227 AC次数:67 平均分:49. ...
- 【BZOJ-3589】动态树 树链剖分 + 线段树 + 线段覆盖(特殊的技巧)
3589: 动态树 Time Limit: 30 Sec Memory Limit: 1024 MBSubmit: 405 Solved: 137[Submit][Status][Discuss] ...
- Gym - 101848C Object-Oriented Programming (树链剖分+线段树+动态开点)
C. Object-Oriented Programming time limit per test 3.0 s memory limit per test 1024 MB input standar ...
- BZOJ 3531 SDOI2014 旅行 树链剖分+线段树动态开点
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3531 题意概述: 给出一棵N个点的树,树上的每个结点有一个颜色和权值,支持以下四种操作: ...
随机推荐
- 腾讯发布新版前端组件框架 Omi,全面拥抱 Web Components
Omi - 合一 下一代 Web 框架,去万物糟粕,合精华为一 → https://github.com/Tencent/omi 特性 4KB 的代码尺寸,比小更小 顺势而为,顺从浏览器的发展和 AP ...
- JVM原理分析
1 什么是JVM? JVM是Java Virtual Machine(Java虚拟机)的缩写,是通过在实际的计算机上仿真模拟各种计算机功能来实现的.由一套字节码指令集.一组寄存器.一个栈.一个垃圾回收 ...
- 软件工程(FZU2015) 赛季得分榜,第二回合
SE_FZU目录:1 2 3 4 5 6 7 8 9 10 11 12 13 积分规则 积分制: 作业为10分制,练习为3分制:alpha30分: 团队项目分=团队得分+个人贡献分 个人贡献分: 个人 ...
- Laravel Providers——服务提供者的注册与启动源码解析
本文 GitBook 地址: https://www.gitbook.com/book/leoyang90/laravel-source-analysishttps://learnku.com/a ...
- [转帖]迎战AMD 7nm 64核EPYC 英特尔至强也玩起了胶水以及性价比
迎战AMD 7nm 64核EPYC 英特尔至强也玩起了胶水以及性价比 Intel 最强CPU 从最开始的双核 到现在的 28核 发展迅猛. https://www.cnbeta.com/article ...
- Oracle增删改查sql语句
--创建表空间 create tablespace waterboss datafile 'd:\waterboss.dbf' size 100m autoextend on next 10m --创 ...
- Jenkins整合SonarQube代码检测工具
借鉴博客:https://blog.csdn.net/kefengwang/article/details/54377055 上面这博客写得挺详细的,挺不错.它这个博客没有提供下载的教程,这个博客提供 ...
- smarTTY总是失败连接的原因
首先用命令 IP addr 查看是否ip 地址错误 事实证明就是因为我的ip地址发生了变化所以导致连接不上, 不过有一次,我将电脑重启 也是连接上了的.
- 认识SQL
一.SQL介绍 SQL 是用于访问和处理数据库的标准的计算机语言. i.What? SQL 指结构化查询语言 SQL 使我们有能力访问数据库 SQL 是一种 ANSI 的标准计算机语言 ii.How? ...
- Hbase数据结构模型