题目链接

(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.1036 [ZJOI2008]树的统计Count ( 点权树链剖分 线段树维护和与最值)

    BZOJ.1036 [ZJOI2008]树的统计Count (树链剖分 线段树维护和与最值) 题意分析 (题目图片来自于 这里) 第一道树链剖分的题目,谈一下自己的理解. 树链剖分能解决的问题是,题目 ...

  2. BZOJ.4034 [HAOI2015]树上操作 ( 点权树链剖分 线段树 )

    BZOJ.4034 [HAOI2015]树上操作 ( 点权树链剖分 线段树 ) 题意分析 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 ...

  3. BZOJ 3672[NOI2014]购票(树链剖分+线段树维护凸包+斜率优化) + BZOJ 2402 陶陶的难题II (树链剖分+线段树维护凸包+分数规划+斜率优化)

    前言 刚开始看着两道题感觉头皮发麻,后来看看题解,发现挺好理解,只是代码有点长. BZOJ 3672[NOI2014]购票 中文题面,题意略: BZOJ 3672[NOI2014]购票 设f(i)f( ...

  4. BZOJ4732. [清华集训2016]数据交互(树链剖分+线段树+multiset)

    题目链接 https://www.lydsy.com/JudgeOnline/problem.php?id=4732 题解 首先,一个比较显然的结论是:对于一棵有根树上的两条链 \((x_1, y_1 ...

  5. BZOJ 1146: [CTSC2008]网络管理Network 树链剖分+线段树+平衡树

    1146: [CTSC2008]网络管理Network Time Limit: 50 Sec  Memory Limit: 162 MBSubmit: 870  Solved: 299[Submit] ...

  6. BZOJ 3672: [Noi2014]购票( 树链剖分 + 线段树 + 凸包 )

    s弄成前缀和(到根), dp(i) = min(dp(j) + (s(i)-s(j))*p(i)+q(i)). 链的情况大家都会做...就是用栈维护个下凸包, 插入时暴力弹栈, 查询时就在凸包上二分/ ...

  7. bzoj 3626 : [LNOI2014]LCA (树链剖分+线段树)

    Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q ...

  8. bzoj 4034: [HAOI2015]树上操作 树链剖分+线段树

    4034: [HAOI2015]树上操作 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 4352  Solved: 1387[Submit][Stat ...

  9. bzoj 1036: [ZJOI2008]树的统计Count 树链剖分+线段树

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 16294  Solved: 6645[Submit ...

随机推荐

  1. 数据库与MySQL进阶(4)

    1,事务 事务指的是满足 ACID 特性的一组操作,可以通过 Commit 提交一个事务,也可以使用 Rollback 进行回滚. 1.1 ACID四大特性 原子性(Atomicity) 事务被视为不 ...

  2. 提高前端开发效率的N种方法

    一.使用固定的html模板和css公共样式 事先把模板建好,每次需要用的时候直接拿来就行,不再需要为浏览器兼容问题考虑太多时间 这里我整理了一套,希望对大家有帮助:http://www.cnblogs ...

  3. epoll、mysql概念及简单操作

    epoll 程序阻塞的过程 假设我们目前运行了三个进程A B C ,如果他们都在处于运行态,那就会被加到一个运行队列中 进程A正在运行socket程序 在linux中有句话,万物皆文件,socket对 ...

  4. 在网页中添加google搜索

    网页中插入谷歌搜索,至于怎么上谷歌,后面有时间会更,推荐百度 <form method="GET" action="http://www.google.com.hk ...

  5. 你不知道的javascript(上卷)读后感(一)

    三剑客 编译,顾名思义,就是源代码执行前会经历的过程,分三个步骤, 分词/词法分析,将我们写的代码字符串分解成多个词法单元 解析/语法分析,将词法单元集合生成抽象语法树(AST) 代码生成,抽象语法树 ...

  6. with语句和空语句

    with语句能够为一组语句创建缺省的对象,在一组语句中,任何不指定对象的属性引用都将被认为是缺省对象. 语法如下: with(object){ statements; } <body> & ...

  7. 一组简单好看的css3渐变按钮

    主要代码如下: body { background:#fff } /* Mixins */ /* bg shortcodes */ .bg-gradient1 span,.bg-gradient1:b ...

  8. java入门学习总结_04

    1.循环结构 2.方法 循环结构 概述 1.对于某些需要重复执行的,相同或者相似的语句,使用某种格式来完成对代码的简化. 2.实现的语句: for语句[常用] while语句[常用] do...whi ...

  9. 华擎 J3455 主板装 Linux 系统

    入手华擎J3455 ITX 主板,装备安装一个 redhat 来学习linux,及做一个家庭 web 服务器.但安装过程一波三折. 问题1.使用U盘引导不了,首先华擎这块板是 UEFI 板,用之前的老 ...

  10. NORDIC BLE MAC ADDR

      一个ble设备,地址可以分成2大类 1.Public Device Address(公共设备地址) 公共设备地址 Public Device Address是48bits的数字,就和电脑mac地址 ...