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 本题中的字典序不同在于空串的字典序最大. 并且题中要求排序后字典序最大. ...
随机推荐
- SQL server 清空数据库所有表
use [数据库名]GOdeclare @sql varchar(8000)while (select count(*) from sysobjects where type='U')>0beg ...
- SourceInsight4破解
2017/10/18 更新 1. 安装原版软件:Source Insight Version 4.0.0092 - March 20, 2018 2. 下载激活程序:sourceinsight_40 ...
- PHP Redis - zSet(有序集合)
有序集合与集合一样,string类型元素的集合,不允许重复的成员. 有序集合,每个元素都会关联一个 double 类型的分数.Redis 通过分数为集合的成员进行从小到大的排序 有序集合的成员是唯一的 ...
- vcsa7 页面出现“no healthy upstream”处理过程
一.昨天服务器出现"no healthy upstream"错误无法登录,搜索网络给出一下的思路: 对于"no healthy upstream"的错误提示,可 ...
- 【收藏】js数组图
- C++ 复习函数的基本知识
C++ 复习函数的基本知识 要使用 C++ 函数,必须完成如下工作: 1. 提供函数定义: 2. 提供函数原型: 3. 调用函数. 例子: #include <iostream> usin ...
- MySql 自动设置时间(自动获取时间,填充时间)
应用场景: 1.在数据表中,要记录每条数据是什么时候创建的,不需要应用程序去特意记录,而由数据数据库获取当前时间自动记录创建时间: 2.在数据库中,要记录每条数据是什么时候修改的,不需要应用程序去特意 ...
- P1296 奶牛的耳语
P1296 奶牛的耳语 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 本题核心思路: 1.读入后要排序以达到剪枝的目的 2.模拟,遇到不能再交流就转入下一头牛,否则计数器加一 3. ...
- Oracle学习-----基本SQL select语句
一.基本select语句 SELECT 标识 选择那些列 FROM 标识从哪个表中选择 select * 标识 全部选择 select department_id, location_id ...
- CSS面试题及答案
介绍一下标准的CSS的盒子模型?与低版本IE的盒子模型有什么不同的? 标准盒子模型:宽度=内容的宽度(content)+ border + padding + margin 低版本IE盒子模型:宽度= ...