P6666 [清华集训2016] 数据交互 题解
##
P6666 [清华集训2016] 数据交互 题解
###
简要题意:
n个点的树,m次操作,分别为添加一条路径$(u_i,v_i,w_i)$,和撤消一条路径,每一次操作后求出一条路径使得与这条路径有交的路径的权值和最大
。
###
题解:
先考虑如何表示所有与一条路径(记为$(u,v)$)有交的路径(记为$(x,y)$)
1.$\mathrm{LCA}(x,y)\in(u,v)$且$\mathrm{LCA}(x,y)\ne \mathrm{LCA}(u,v)$
2.$\mathrm{LCA}(u,v)\in(x,y)$
于是我们可以记$w_i$表示所有$\mathrm{LCA}(u,v)=i$的权值和,$g_i$表示所有 $i\in(u,v)$的路径的权值和。然后我们可以把一条路径有交的权值和写成非常漂亮的形式:路径上除了 $\rm LCA$的$w_i$和$g_{\mathrm{LCA}}$。
这样每次修改时就只用改$w_{\mathrm{LCA}}$和其他点的$g_i$的值。
首先我们给每个点 v 开一个可删除堆表示v的虚子树中到 v 的最长链,再开一个全局的可删除堆记录每条重链贡献的答案。
我们发现,一条路径可以看成是重链上的$u,v$两点(其中$u$是$\mathrm{LCA}$),以及这两点分别向虚子树伸出去的一条最长链(可以为空)。它的权值为这条链的$w_i$之和再加上$g_u$,当$u$等于$v$的时候,我们需要找最长链和次长链。
然后我们发现这就是一个最大子段和的问题。对于线段树上的每一个节点,我们维护:
$maxl$:从前节点包含的区间最左边(深度最小)的结点开始的最长链。
$maxr$: 从前节点包含的区间最右边(深度最小)的结点开始的最长链。
$maxn$: 当前区间的答案
$sum$: 当前区间的$w_i$之和
一条重链的链顶的最长链就是这条重链对应的$maxl$。
在修改的时候,我们先删除$lca$以上$log$条重链对答案的贡献,然后修改链上点的$g$和$lca$的$w$,再更新答案。
代码:(略微有点长)
``
#include<bits/stdc++.h>
#define ll long long
#define mid ((l+r)>>1)
#define ls (rt<<1)
#define rs (rt<<1|1)
using namespace std;
inline int read(){
int s=0;
char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return s;
}
inline void write(ll x){
if(x>=10)write(x/10);
putchar(x%10+48);
}
const int N=100501;
int n,m;
vector<int>e[N];
int fa[N],dep[N],tp[N],tot,dfn[N],siz[N],son[N],bot[N],id[N];
struct HEAP{
priority_queue<ll>del,q;
inline void POP(){
while(del.size()&&del.top()==q.top()){
del.pop(),q.pop();
}
}
inline void PUSH(ll x){
POP();
q.push(x);
}
inline void DEL(ll x){
del.push(x);
}
inline ll fi(){
POP();
return q.top();
}
inline ll se(){
ll x=fi();
DEL(x);
ll y=fi();
PUSH(x);
return y;
}
}ANS,q[N];
struct node{
int l,r;
ll maxl,maxr,maxn,sum,lz;
node(){l=r=maxl=maxr=maxn=sum=lz=0;}
};
node operator +(const node &a,const node &b){
node c;
c.l=a.l,c.r=b.r;
c.maxn=max(max(a.maxn,b.maxn),a.maxr+b.maxl);
c.maxl=max(a.maxl,a.sum+b.maxl);
c.maxr=max(b.maxr,b.sum+a.maxr);
c.sum=a.sum+b.sum;
return c;
}
struct SEG{
node t[N<<2];
inline void upd(int rt,int p){
int x=id[p];
ll fi=q[x].fi(),se=q[x].se();
t[rt].maxl=fi+t[rt].sum;
t[rt].maxr=fi+t[rt].sum+t[rt].lz;
t[rt].maxn=fi+se+t[rt].sum+t[rt].lz;
}
inline void build(int rt,int l,int r){
if(l==r){
upd(rt,l);
return;
}
build(ls,l,mid),build(rs,mid+1,r);
t[rt]=t[ls]+t[rs];
}
inline void psh(int rt,int x){
t[rt].maxn+=x,t[rt].maxr+=x,t[rt].lz+=x;
}
inline void pshlz(int rt){
if(t[rt].lz){
psh(ls,t[rt].lz),psh(rs,t[rt].lz);
t[rt].lz=0;
}
}
inline void modify(int rt,int l,int r,int L,int R,ll x){
if(L<=l&&r<=R){
psh(rt,x);
return;
}
pshlz(rt);
if(L<=mid)modify(ls,l,mid,L,R,x);
if(mid<R)modify(rs,mid+1,r,L,R,x);
t[rt]=t[ls]+t[rs];
}
inline void modify(int rt,int l,int r,int x,ll p){
t[rt].sum+=p;
if(l==r){
t[rt].maxr+=p,t[rt].maxn+=p,t[rt].maxl+=p;
return;
}
pshlz(rt);
if(x<=mid)modify(ls,l,mid,x,p);
else modify(rs,mid+1,r,x,p);
t[rt]=t[ls]+t[rs];
}
inline void change(int rt,int l,int r,int x){
if(l==r){
upd(rt,x);
return;
}
pshlz(rt);
if(x<=mid)change(ls,l,mid,x);
else change(rs,mid+1,r,x);
t[rt]=t[ls]+t[rs];
}
inline node query(int rt,int l,int r,int L,int R){
if(L<=l&&r<=R)return t[rt];
pshlz(rt);
if(L>mid)return query(rs,mid+1,r,L,R);
else if(R<=mid)return query(ls,l,mid,L,R);
else return query(ls,l,mid,L,mid)+query(rs,mid+1,r,mid+1,R);
}
}T;
struct queryoption{
int x,y,w;
}op[N];
inline void dfs(int x){
siz[x]=1;
for(auto y:e[x]){
if(y!=fa[x]){
fa[y]=x;
dep[y]=dep[x]+1;
dfs(y);
siz[x]+=siz[y];
if(siz[y]>siz[son[x]])son[x]=y;
}
}
}
inline void dfs1(int x,int topp){
dfn[x]=++tot;
id[tot]=x;
tp[x]=topp;
bot[x]=x;
if(son[x])dfs1(son[x],topp),bot[x]=bot[son[x]];
for(auto y:e[x]){
if(y!=fa[x]&&y!=son[x])dfs1(y,y);
}
}
inline int get_lca(int u,int v){
while(tp[u]!=tp[v]){
if(dep[tp[u]]<dep[tp[v]])swap(u,v);
u=fa[tp[u]];
}
return dep[u]<dep[v]?u:v;
}
inline void add(int u,int v){
node c;
while(tp[u]!=tp[v]){
if(dep[tp[u]]<dep[tp[v]])swap(u,v);
c=T.query(1,1,n,dfn[tp[u]],dfn[bot[u]]);
ANS.PUSH(c.maxn);
u=fa[tp[u]];
}
c=T.query(1,1,n,dfn[tp[u]],dfn[bot[u]]);
ANS.PUSH(c.maxn);
}
inline void del(int u,int v){
node c;
while(tp[u]!=tp[v]){
if(dep[tp[u]]<dep[tp[v]])swap(u,v);
c=T.query(1,1,n,dfn[tp[u]],dfn[bot[u]]);
ANS.DEL(c.maxn);
u=fa[tp[u]];
}
c=T.query(1,1,n,dfn[tp[u]],dfn[bot[u]]);
ANS.DEL(c.maxn);
}
inline void modify(int u,int v,int w){
while(tp[u]!=tp[v]){
if(dep[tp[u]]<dep[tp[v]])swap(u,v);
T.modify(1,1,n,dfn[tp[u]],dfn[u],w);
u=fa[tp[u]];
}
if(dep[u]>dep[v])swap(u,v);
T.modify(1,1,n,dfn[u],dfn[v],w);
}
inline void solve(int u,int v,int w){
int lca=get_lca(u,v);
node c;
for(int i=tp[lca];i;i=tp[fa[i]]){
c=T.query(1,1,n,dfn[i],dfn[bot[i]]);
if(fa[i])q[fa[i]].DEL(c.maxl);
if(i!=tp[lca]){
ANS.DEL(c.maxn);
}
}
del(u,v);
T.modify(1,1,n,dfn[lca],w);
modify(u,v,w);
modify(lca,lca,-w);
add(u,v);
for(int i=tp[lca];i;i=tp[fa[i]]){
c=T.query(1,1,n,dfn[i],dfn[bot[i]]);
if(fa[i])q[fa[i]].PUSH(c.maxl),T.change(1,1,n,dfn[fa[i]]);
if(i!=tp[lca]){
ANS.PUSH(c.maxn);
}
}
}
int main(){
n=read(),m=read();
for(int i=1;i<n;i++){
int x=read(),y=read();
e[x].push_back(y),e[y].push_back(x);
}
for(int i=1;i<=n;i++)q[i].PUSH(0),q[i].PUSH(0);
dfs(1);
dfs1(1,1);
for(int i=1;i<=n;i++)if(i==tp[i])ANS.PUSH(0);
char ch;
T.build(1,1,n);
for(int i=1;i<=m;i++){
while(ch=getchar(),ch!='-'&&ch!='+');
if(ch=='+'){
op[i].x=read(),op[i].y=read(),op[i].w=read();
solve(op[i].x,op[i].y,op[i].w);
}
else{
int x=read();
solve(op[x].x,op[x].y,-op[x].w);
}
write(ANS.fi());
puts("");
}
return 0;
}
P6666 [清华集训2016] 数据交互 题解的更多相关文章
- BZOJ4732. [清华集训2016]数据交互(树链剖分+线段树+multiset)
题目链接 https://www.lydsy.com/JudgeOnline/problem.php?id=4732 题解 首先,一个比较显然的结论是:对于一棵有根树上的两条链 \((x_1, y_1 ...
- BZOJ 4732 UOJ #268 [清华集训2016]数据交互 (树链剖分、线段树)
题目链接 (BZOJ) https://www.lydsy.com/JudgeOnline/problem.php?id=4732 (UOJ) http://uoj.ac/problem/268 题解 ...
- UOJ268 [清华集训2016] 数据交互 【动态DP】【堆】【树链剖分】【线段树】
题目分析: 不难发现可以用动态DP做. 题目相当于是要我求一条路径,所有与路径有交的链的代价加入进去,要求代价最大. 我们把链的代价分成两个部分:一部分将代价加入$LCA$之中,用$g$数组保存:另一 ...
- [UOJ#276]【清华集训2016】汽水
[UOJ#276][清华集训2016]汽水 试题描述 牛牛来到了一个盛产汽水的国度旅行. 这个国度的地图上有 \(n\) 个城市,这些城市之间用 \(n−1\) 条道路连接,任意两个城市之间,都存在一 ...
- [UOJ#274][清华集训2016]温暖会指引我们前行
[UOJ#274][清华集训2016]温暖会指引我们前行 试题描述 寒冬又一次肆虐了北国大地 无情的北风穿透了人们御寒的衣物 可怜虫们在冬夜中发出无助的哀嚎 “冻死宝宝了!” 这时 远处的天边出现了一 ...
- bzoj 4736 /uoj274【清华集训2016】温暖会指引我们前行 lct
[清华集训2016]温暖会指引我们前行 统计 描述 提交 自定义测试 寒冬又一次肆虐了北国大地 无情的北风穿透了人们御寒的衣物 可怜虫们在冬夜中发出无助的哀嚎 “冻死宝宝了!” 这时 远处的天边出现了 ...
- 【UOJ274】【清华集训2016】温暖会指引我们前行 LCT
[UOJ274][清华集训2016]温暖会指引我们前行 任务描述 虽然小R住的宿舍楼早已来了暖气,但是由于某些原因,宿舍楼中的某些窗户仍然开着(例如厕所的窗户),这就使得宿舍楼中有一些路上的温度还是很 ...
- uoj266[清华集训2016]Alice和Bob又在玩游戏(SG函数)
uoj266[清华集训2016]Alice和Bob又在玩游戏(SG函数) uoj 题解时间 考虑如何求出每棵树(子树)的 $ SG $ . 众所周知一个状态的 $ SG $ 是其后继的 $ mex $ ...
- UOJ #274. 【清华集训2016】温暖会指引我们前行 [lct]
#274. [清华集训2016]温暖会指引我们前行 题意比较巧妙 裸lct维护最大生成树 #include <iostream> #include <cstdio> #incl ...
- UOJ_274_[清华集训2016]温暖会指引我们前行_LCT
UOJ_274_[清华集训2016]温暖会指引我们前行_LCT 任务描述:http://uoj.ac/problem/274 本题中的字典序不同在于空串的字典序最大. 并且题中要求排序后字典序最大. ...
随机推荐
- 全局 使用 stylus 变量
首先 安装 啦 npm i stylus stylus-loader -D 之后 使用全局变量 然后在组件中引用改变量 -->没有用! 原因还没有在build/utils.js文件 全局引入 ...
- 浏览器垃圾回收机制:栈垃圾回收、堆垃圾回收、新生区老生区、Scavenge算法、标记-清除算法、标记-整理算法、全停顿、增量标记
浏览器垃圾回收机制根据数据的存储方式分为栈垃圾回收和堆垃圾回收. 栈垃圾回收的方式非常简便,当一个函数执行结束之后,JavaScript 引擎会通过向下移动 ESP 来销毁该函数保存在栈中的执行上下文 ...
- php后端遇到的问题
1.用文件记录日志,会有并发问题
- 笔记:map函数方法映射
------------恢复内容开始------------ map函数可以对后端接口返回一个数组 map存在一个参数时: map存在三个参数时: 语法: array.map(function(cur ...
- 4、Redis底层原理(持久化+分布式锁)
Redis底层原理 持久化 Redis虽然是个内存数据库,但是Redis支持RDB和AOF两种持久化机制,将数据写往磁盘,可以有效地避免因进程退出造成的数据丢失问题,当下次重启时利用之前持久化的文件即 ...
- Alibaba Cloud Linux 3.2104 64位安装mysql5.6.45
1 .安装cmake wget http://www.cmake.org/files/v2.8/cmake-2.8.10.2.tar.gz tar -zxvf cmake-2.8.10.2.tar.g ...
- SSM PUT请求导致的400,415,500问题
最近在尝试用PUT方法的请求时一直产生400,415,500错误,弄了半天(真的是半天),尝试了各种办法,现在终于解决了,为了防止忘记,在此记录下 下面是一步步解决的步骤.(还有许多我略过了)如果只想 ...
- Ajax属性
如何创建一个 Ajax <script> window.onload=function(){ 1)创建一个异步调用对象 va ...
- Java定时器Timer和TimerTask
方式一:设定指定任务task在指定时间time执行 schedule(TimerTask task, Date date) public static void main(String[] arg ...
- The Ultimate Guide to Dynamics 365 Pricing and Licensing
Microsoft Dynamics 365 integrates powerful ERP and CRM capabilities in the cloud to provide busi ...