题目链接

(BZOJ) https://www.lydsy.com/JudgeOnline/problem.php?id=4732
(UOJ) http://uoj.ac/problem/268

题解

首先考虑,给定一条路径,如何计算与其相交的所有路径的权值和?显然一条路径和另一条路径相交,当且仅当这条路径的LCA在另一条路径上,或者另一条路径的LCA在这条路径上。那么我们考虑维护两个数组\(a\)和\(b\), 分别表示以某点为LCA的路径权值和以及覆盖这个点但不以该点为LCA的路径权值和,则路径\((u,v)\)的价值为\(\sum_{p \in (u,v)}b_p+a_{LCA(u,v)}\).
下面我们要求路径的最大权值,考虑链分治。在一条重链上统计LCA在这条重链上的链的答案。假设一条链和该重链相交的部分为\((u,v)\)其中\(u\)是\(v\)的祖先也是链的LCA。若\(u\ne v\), 该链的权值为\(h_u+h_v+\sum^v_{i=u}a_i+b_u\),否则为\(h_u+h'_u+a_u+b_u\), 其中\(h_u\)和\(h'_u\)分别为\(u\)点的轻儿子引出的链的\(\sum a_i\)的最大值和次大值。一条重链的权值等于上面的式子在\(u\)和\(v\)变化时的最大值,考虑维护这个最大值,对于\(u\ne v\)的情况,需要采取最大子段和的维护方式;对于\(u=v\)的情况,就是简单的区间修改区间最大值,都可以用线段树维护。总答案等于所有重链的权值最大值,因此我们开一个multiset维护所有重链的权值。
考虑修改。修改时我们需要把一条链上的\(b\)增加一个权值\(w\)(可能是负数),再把LCA处的\(a\)增加一个权值\(w\). 修改\(b\)在重链上是区间修改,这个可以简单地进行打标记,把后缀和和最大子段和加上某一个值。修改\(a\)在重链上是单点修改。但是一个\(a\)的修改还会影响到它所在重链顶端的父亲的\(h\), 那个点的\(h\)改变又会影响它所在重链顶端的父亲的\(h\)……直到根,因此需要不断跳重链修改。这条链的\(\sum a_i\)最大值就相当于我们在线段树中维护的“最大前缀和”那个量(\(\sum^v_{i=u}a_i+h_v\)), 因此可以直接重新查出,设为\(sa_i\). 为了快速维护\(sa\)的最大次大,我们只需对每个点再开一个multiset记录一下即可。每次对一条链进行修改后,需要重新计算这条链的权值,更新总答案multiset.
时间复杂度\(O(n\log^2n)\).
注意multiset用两个堆实现会快,线段树给每条链开一棵比\([1,n]\)开一棵要快。

代码

人丑常数大……

#include<bits/stdc++.h>
#define llong long long
using namespace std;inline int read()
{int w=1,s=0;char ch=getchar();while(!isdigit(ch)) {if(ch=='-')w=-1;ch=getchar();}while(isdigit(ch)) {s=s*10+ch-'0';ch=getchar();}return w*s;
}const int N = 1e5;
struct Edge
{int nxt,v;
} e[(N<<1)+3];
struct Query
{int u,v,w;
} qr[N+3];
int fe[N+3];
int fa[N+3];
int dfn[N+3],idfn[N+3];
int dep[N+3];
int sz[N+3];
int hvs[N+3];
int tpn[N+3],btn[N+3];
llong a[N+3],h[N+3],h2[N+3],hv[N+3],sa[N+3];
int n,q,en,dfnn;struct Multiset
{priority_queue<llong> q1,q2;void insert(llong x) {q1.push(x);}void erase(llong x) {q2.push(x);}llong getmx(){while(!q2.empty()&&q1.top()==q2.top()) {q1.pop(),q2.pop();}return q1.top();}llong getmx2(){llong mx = getmx(); erase(mx); llong ret = getmx(); insert(mx); return ret;}
};
Multiset lt[N+3]; int ltn[N+3];
Multiset s;void addedge(int u,int v)
{en++; e[en].v = v;e[en].nxt = fe[u]; fe[u] = en;
}void dfs1(int u)
{sz[u] = 1; hvs[u] = 0;for(int i=fe[u]; i; i=e[i].nxt){int v = e[i].v;if(v==fa[u]) continue;fa[v] = u;dep[v] = dep[u]+1;dfs1(v);sz[u] += sz[v];if(hvs[u]==0||sz[v]>sz[hvs[u]]) {hvs[u] = v;}}
}
void dfs2(int u)
{dfn[u] = ++dfnn; idfn[dfnn] = u;if(!hvs[u]) {btn[u] = u; s.insert(0ll); return;}tpn[hvs[u]] = tpn[u]; dfs2(hvs[u]); btn[u] = btn[hvs[u]];for(int i=fe[u]; i; i=e[i].nxt){int v = e[i].v;if(v==fa[u]||v==hvs[u]) continue;lt[u].insert(0); ltn[u]++;tpn[v] = v;dfs2(v);}
}struct Data
{llong v,lv,rv,lrv;Data() {v = lv = rv = lrv = 0ll;}Data(llong _v,llong _lv,llong _rv,llong _lrv):v(_v),lv(_lv),rv(_rv),lrv(_lrv) {}
};
Data operator +(const Data &x,const Data &y)
{Data ret;ret.lrv = x.lrv+y.lrv;ret.lv = max(x.lv,x.lrv+y.lv);ret.rv = max(y.rv,x.rv+y.lrv);ret.v = max(max(x.v,y.v),x.rv+y.lv);return ret;
}
struct SegmentTree
{struct SgTNode{Data x; llong tag;} sgt[(N<<2)+3];void maketag(int u,llong tag){sgt[u].x.rv += tag; sgt[u].x.v += tag;sgt[u].tag += tag;}void pushdown(int u){llong tag = sgt[u].tag;if(tag){maketag(u<<1,tag); maketag(u<<1|1,tag);sgt[u].tag = 0ll;}}void upd(int u,int le,int ri,int pos){if(le==ri) {int pos0 = idfn[pos]; sgt[u].x = Data(a[pos0]+sgt[u].tag+h[pos0],a[pos0]+h[pos0],a[pos0]+sgt[u].tag+h[pos0],a[pos0]); return;}pushdown(u);int mid = (le+ri)>>1;if(pos<=mid) upd(u<<1,le,mid,pos);else upd(u<<1|1,mid+1,ri,pos);sgt[u].x = sgt[u<<1].x+sgt[u<<1|1].x;}void addb(int u,int le,int ri,int lb,int rb,llong x){if(le>=lb && ri<=rb) {maketag(u,x); return;}pushdown(u);int mid = (le+ri)>>1;if(lb<=mid) addb(u<<1,le,mid,lb,rb,x);if(rb>mid) addb(u<<1|1,mid+1,ri,lb,rb,x);sgt[u].x = sgt[u<<1].x+sgt[u<<1|1].x;}Data query(int u,int le,int ri,int lb,int rb){if(le>=lb && ri<=rb) {return sgt[u].x;}pushdown(u);int mid = (le+ri)>>1; Data ret;if(rb<=mid) ret = query(u<<1,le,mid,lb,rb);else if(lb>mid) ret = query(u<<1|1,mid+1,ri,lb,rb);else ret = query(u<<1,le,mid,lb,rb)+query(u<<1|1,mid+1,ri,lb,rb);sgt[u].x = sgt[u<<1].x+sgt[u<<1|1].x;return ret;}
} sgt;
struct SegmentTree2
{struct SgTNode{llong tag,mx;} sgt[(N<<2)+3];void maketag(int u,llong tag){sgt[u].mx += tag; sgt[u].tag += tag;}void pushdown(int u){llong tag = sgt[u].tag;if(tag){maketag(u<<1,tag); maketag(u<<1|1,tag);sgt[u].tag = 0;}}void add(int u,int le,int ri,int lb,int rb,llong x){if(le>=lb && ri<=rb) {maketag(u,x); return;}pushdown(u);int mid = (le+ri)>>1;if(lb<=mid) add(u<<1,le,mid,lb,rb,x);if(rb>mid) add(u<<1|1,mid+1,ri,lb,rb,x);sgt[u].mx = max(sgt[u<<1].mx,sgt[u<<1|1].mx);}llong query(int u,int le,int ri,int lb,int rb){if(le>=lb && ri<=rb) {return sgt[u].mx;}pushdown(u);int mid = (le+ri)>>1; llong ret = 0ll;if(lb<=mid) ret = max(ret,query(u<<1,le,mid,lb,rb));if(rb>mid) ret = max(ret,query(u<<1|1,mid+1,ri,lb,rb));sgt[u].mx = max(sgt[u<<1].mx,sgt[u<<1|1].mx);return ret;}
} sgt2;void calcpath(int u)
{int x = tpn[u],y = btn[u];s.erase(hv[x]);Data tmp = sgt.query(1,1,n,dfn[x],dfn[y]); hv[x] = tmp.v;llong tmp2 = sgt2.query(1,1,n,dfn[x],dfn[y]); hv[x] = max(hv[x],tmp2);s.insert(hv[x]);
}
void addvpath(int u,int v,llong w)
{sgt.addb(1,1,n,dfn[u],dfn[v],w);sgt2.add(1,1,n,dfn[u],dfn[v],w);calcpath(u);
}
void addpath(int u0,int v0,llong w)
{int u = u0,v = v0;while(tpn[u]!=tpn[v]){if(dep[tpn[u]]>dep[tpn[v]]) {addvpath(tpn[u],u,w); u = fa[tpn[u]];}else {addvpath(tpn[v],v,w); v = fa[tpn[v]];}}if(dep[u]>dep[v]) swap(u,v);if(u!=v) {addvpath(idfn[dfn[u]+1],v,w);}a[u] += w; sgt.upd(1,1,n,dfn[u]); sgt2.add(1,1,n,dfn[u],dfn[u],w); calcpath(u);int x = fa[tpn[u]];while(x){lt[x].erase(sa[tpn[u]]);Data tmp = sgt.query(1,1,n,dfn[tpn[u]],dfn[btn[u]]); sa[tpn[u]] = tmp.lv;lt[x].insert(sa[tpn[u]]);llong tmp1 = h[x]; h[x] = lt[x].getmx(); sgt.upd(1,1,n,dfn[x]); sgt2.add(1,1,n,dfn[x],dfn[x],h[x]-tmp1);if(ltn[x]>=2){llong tmp2 = h2[x]; h2[x] = lt[x].getmx2();sgt2.add(1,1,n,dfn[x],dfn[x],h2[x]-tmp2);}calcpath(x);u = x; x = fa[tpn[x]];}
}int main()
{scanf("%d%d",&n,&q);for(int i=1; i<n; i++){int u,v; scanf("%d%d",&u,&v);addedge(u,v); addedge(v,u);}dep[1] = 1; dfs1(1);tpn[1] = 1; dfs2(1);for(int i=1; i<=q; i++){char opt[5]; scanf("%s",opt); int u,v,w;if(opt[0]=='+') {scanf("%d%d%d",&qr[i].u,&qr[i].v,&qr[i].w); u = qr[i].u,v = qr[i].v,w = qr[i].w;}else {int id; scanf("%d",&id); u = qr[id].u,v = qr[id].v,w = -qr[id].w;}addpath(u,v,w);printf("%lld\n",s.getmx());}return 0;
}

BZOJ 4732 UOJ #268 [清华集训2016]数据交互 (树链剖分、线段树)相关推荐

  1. BZOJ 4811 树链剖分+线段树

    思路: 感觉这题也可神了.. (还是我太弱) 首先发现每一位不会互相影响,可以把每一位分开考虑,然后用树链剖分或者LCT维护这个树 修改直接修改,询问的时候算出来每一位填0,1经过这条链的变换之后得到 ...

  2. UOJ #268 BZOJ 4732 [清华集训2016]数据交互 (树链剖分、线段树)

    题目链接 (BZOJ) https://www.lydsy.com/JudgeOnline/problem.php?id=4732 (UOJ) http://uoj.ac/problem/268 题解 ...

  3. BZOJ 4734 UOJ #269 [清华集训2016]如何优雅地求和 (多项式)

    题目链接 (BZOJ) https://www.lydsy.com/JudgeOnline/problem.php?id=4734 (UOJ) http://uoj.ac/problem/269 题解 ...

  4. 树链剖分——线段树区间合并bzoj染色

    线段树区间合并就挺麻烦了,再套个树链就更加鬼畜,不过除了代码量大就没什么其他的了.. 一些细节:线段树每个结点用结构体保存,pushup等合并函数改成返回一个结构体,这样好写一些 struct Seg ...

  5. BZOJ 2836 树链剖分+线段树

    思路: 链剖+线段树裸题 重链的标号就是DFS序 所以查子树的时候每回就 query(change[x],change[x]+size[x]-1) 就好了 剩下的应该都会吧.. //By Sirius ...

  6. bzoj 4127: Abs(树链剖分+线段树)

    4127: Abs Time Limit: 40 Sec  Memory Limit: 256 MB Submit: 667  Solved: 225 [Submit][Status][Discuss ...

  7. bzoj 4034: [HAOI2015]树上操作(树链剖分+线段树区间更新)

    4034: [HAOI2015]树上操作 Time Limit: 10 Sec  Memory Limit: 256 MB Submit: 4981  Solved: 1603 [Submit][St ...

  8. bzoj 3083 遥远的国度——树链剖分+线段树维护子树信息

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3083 int 的范围是 2^31 - 1 ,所以权值是不是爆 int 了-- O( nlog ...

  9. bzoj 3730: 震波 动态点分治+树链剖分+线段树

    ##### 题目描述 : 在一片土地上有N个城市,通过N-1条无向边互相连接,形成一棵树的结构,相邻两个城市的距离为1,其中第i个城市的价值为value[i]. 不幸的是,这片土地常常发生地震,并且随 ...

最新文章

  1. 文巾解题 461. 汉明距离
  2. 微机原理及接口技术-6
  3. 阿里巴巴对Java编程【OOP规约】的规约
  4. H264 解码耗时分析
  5. 大数据成长之路:谈谈那些必须学习的Linux基础知识
  6. sqlplus 调试存储过程
  7. python 两点曲线_全方位比较3种数据科学工具的比较:Python、R和SAS(附链接)
  8. 【基于obs插件-5】-屏幕截图
  9. 求一亿以内的回文质数(素数)
  10. 【爬虫实战】起点中文网小说的爬取
  11. 云桌面 瘦终端_一体机+瘦终端=桌面云,云平台搭建更便捷,桌面虚拟化
  12. 系统与软件过程改进09年年会,CMMI vs 敏捷PK赛参赛感言
  13. 李南江HTML5教程学习心得
  14. JavaScript进阶 | DOM
  15. 9.28 正睿普及3
  16. 和开源硬件相关的几个词,免费、山寨、创客教育,以及未来 | COSCon'18
  17. 直接打印RAW文件到打印机
  18. WordPress 网站怎么做会员中心功能【会员中心】
  19. C# 面向对象-面向对象概述
  20. 2026:【例4.12】阶乘和

热门文章

  1. Qt小传——从诞生到发展、繁荣
  2. 61条面向对象设计的经验原则
  3. 使用socket创建服务器进程和客户端进程
  4. 跨站脚本(XSS)漏洞 (一)
  5. SpringBoot对于标注@ResponseBody注解返回JSON数据的处理
  6. Java中equals、==和hashcode()
  7. uboot 详细注释讲解
  8. Android PC投屏简单尝试- 自定义协议章(Socket+Bitmap)
  9. Android Linker学习笔记
  10. python中链表和数组_Python