【HDU 6973】Bookshop 树剖+平衡树

【引言】

​ 平衡树的题做得比较少,难得补一次神题,记录一下供以后学习。

【题意】

​ 给出一棵 nnn 个结点的树,每个结点有一个价值为 pip_ipi​ 的商品。

​ 有 mmm 次询问,每次问如果一个人带着 www 块钱,从 xxx 结点出发到达 yyy 结点结束,在路上遇到能买的东西就买,不能买就继续往前走,等他走完这段路之后,还剩多少钱?(询问之间相互独立。)

​ 数据范围:n,m≤105n,m\le 10^5n,m≤105 , pi,w≤109p_i,w\le 10^9pi​,w≤109

【分析】

​ 容易想到树剖把一个询问的问题分段。

​ 假设可以把 x→yx\to yx→y 分成 x→z,znext→yx\to z,z_{next}\to yx→z,znext​→y 这连续的两段,那么钱的剩余值 remain(x,y,w)=remain(znext,y,remain(x,z,w))remain(x,y,w) =remain(z_{next},y,remain(x,z,w))remain(x,y,w)=remain(znext​,y,remain(x,z,w))

​ 也就是说分段之后,只要能按顺序解决每一段的问题,就能解决整个询问的问题。

​ 那么树剖就可以把一个询问分成 O(log⁡n)O(\log n)O(logn) 段,我们还可以让重链上的 dfsdfsdfs 序连续。

​ 对每个询问,一定都是先从 xxx 走若干逆 dfsdfsdfs 序的连续段,然后到达 LCALCALCA ,再从 LCALCALCA 走若干顺 dfsdfsdfs 序的连续段到达 yyy 。

​ 于是,我们把每个询问剖出来的每个连续段分为正逆两类。

​ 然后,一起做所有询问的逆 dfsdfsdfs 序部分,也就是说按照逆 dfsdfsdfs 序扫描每个商品,遇到了一个询问的起点,就把这个询问对应的起始金钱丢入一个数据结构中,过了一个询问的中点,就把这个询问对应的剩余金钱从数据结构中取出来。然后每扫过一个 pip_ipi​ ,数据结构都要对内部 [pi,+∞)[p_i,+\infin)[pi​,+∞) 的键值进行减 pip_ipi​ 。

​ 可以用平衡树做这个数据结构。

​ 区间减的时候分为两部分,对于 [pi,2pi)[p_i,2p_i)[pi​,2pi​) 的键值,暴力取出来然后改值之后塞进去,均摊每个值最多操作 O(log⁡w)O(\log w)O(logw) 次。

​ 对于 [2pi,+∞)[2p_i,+\infin)[2pi​,+∞) 直接打 tag 维护,因为减掉之后互不影响相对位置。

​ 逆 dfsdfsdfs 序部分做完之后,类似的做一遍顺 dfsdfsdfs 序就行了。

​ 总复杂度可以这么计算,每个询问变成了 O(log⁡n)O(\log n)O(logn) 段,但是题目性质保证我们在扫描的时候平衡树的 sizesizesize 是 O(n)O(n)O(n) ,平衡树的操作还是 O(log⁡n)O(\log n)O(logn) 的,再算上每个询问有 O(log⁡w)O(\log w)O(logw) 次平衡树上的暴力修改,总复杂度大致是 O(nlog⁡2n+nlog⁡nlog⁡w)O(n\log^2 n +n\log n\log w )O(nlog2n+nlognlogw)

​ 最后我用的 FHQ Treap\text{FHQ Treap}FHQ Treap 实现的,代码长度有点窒息。

​ 要注意一下 FHQ Treap\text{FHQ Treap}FHQ Treap 的插入,需要硬拆开再合并。

#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXN 100011
#define LL long long
using namespace std;
int n,m;
int tot,pre[MAXN],lin[MAXN*2],to[MAXN*2];
int fa[MAXN],son[MAXN],dep[MAXN],siz[MAXN],dfn[MAXN],anti_dfn[MAXN];
int num;
int top[MAXN];
int p[MAXN];
struct Query{int st,ed;int w,id,tree_node;
}q[MAXN];
struct WALK{bool is_st;int loc;int belong;WALK(){}WALK(int _is_st,int _loc,int _belong):is_st(_is_st),loc(_loc),belong(_belong){}
};
bool cmp(WALK x,WALK y)
{return x.loc==y.loc?x.is_st > y.is_st : x.loc < y.loc;
}
bool rev_cmp(WALK x,WALK y)
{return x.loc==y.loc?x.is_st > y.is_st : x.loc > y.loc;
}
vector<WALK > vec,rev_vec;void add(int x,int y)
{tot++;lin[tot]=pre[x];pre[x]=tot;to[tot]=y;
}
void dfs1(int x)
{siz[x]=1;for(int i=pre[x];i;i=lin[i]){int v=to[i];if(v==fa[x])continue;fa[v]=x;dep[v]=dep[x]+1;dfs1(v);siz[x]+=siz[v];if(siz[son[x]]<siz[v])son[x]=v;}
}
void dfs2(int x)
{dfn[x]=++num;anti_dfn[num]=x;if(x==son[fa[x]])top[x]=top[fa[x]];elsetop[x]=x;if(son[x])dfs2(son[x]);for(int i=pre[x];i;i=lin[i]){int v=to[i];if(v==fa[x]||v==son[x])continue;dfs2(v);}
}
void LCA(int x,int y,int belong)
{while(top[x]!=top[y]){if(dep[top[x]] > dep[top[y]]){rev_vec.push_back(WALK(true,dfn[x],belong));rev_vec.push_back(WALK(false,dfn[top[x]],belong));x=fa[top[x]];}else{vec.push_back(WALK(true,dfn[top[y]],belong));vec.push_back(WALK(false,dfn[y],belong));y=fa[top[y]];}}if(dep[x] > dep[y]){rev_vec.push_back(WALK(true,dfn[x],belong));rev_vec.push_back(WALK(false,dfn[y],belong));}else{vec.push_back(WALK(true,dfn[x],belong));vec.push_back(WALK(false,dfn[y],belong));}
}
struct QUEUE{int q[MAXN],head,tail;QUEUE(){head=tail=0;}bool empty(){return tail==head;}void pop(){head++;if(head==MAXN)head=0;}void push(int x){q[tail++]=x;if(tail==MAXN)tail=0;}int front(){return q[head];}void clear(){head=tail=0;}
};
struct FHQ_Treap{static const int Null = 0;QUEUE que;int num,root;struct node{int l,r,fa;int rnd,val,belong,siz,tag;node(){siz=tag=0;}}tr[MAXN*2];void init(){num=0;root=Null;que.clear();}void pushup(int x){tr[x].siz=tr[tr[x].l].siz+tr[tr[x].r].siz+1;tr[tr[x].l].fa=tr[tr[x].r].fa=x;}void pushdown(int x){if(tr[x].tag){int L=tr[x].l,R=tr[x].r;if(L){tr[L].val+=tr[x].tag;tr[L].tag+=tr[x].tag;}if(R){tr[R].val+=tr[x].tag;tr[R].tag+=tr[x].tag;}tr[x].tag=0;}}inline int rnd(){return rand();}int add(int val,int belong){int now;if(!que.empty()){now=que.front();que.pop();}elsenow = ++num;q[belong].tree_node=now;tr[now].l=tr[now].r=Null;tr[now].rnd=rnd();tr[now].siz=1;tr[now].val=val;tr[now].belong=belong;return now;}void split_val(int x,int k,int &l,int &r){if(!x){l=r=Null;return;}pushdown(x);if(tr[x].val<=k){l=x;split_val(tr[x].r,k,tr[x].r,r);}else {r=x;split_val(tr[x].l,k,l,tr[x].l);}pushup(x);}void split_rank(int x,int k,int &l,int &r){if(!x){l=r=Null;return;}pushdown(x);if(tr[tr[x].l].siz+1<=k){l=x;split_rank(tr[x].r,k-tr[tr[x].l].siz-1,tr[x].r,r);}else{r=x;split_rank(tr[x].l,k,l,tr[x].l);}pushup(x);}int merge(int x,int y){if(!x||!y)return x|y;if(tr[x].rnd < tr[y].rnd){pushdown(x);tr[x].r=merge(tr[x].r,y);pushup(x);return x;}else{pushdown(y);tr[y].l=merge(x,tr[y].l);pushup(y);return y;}}void insert(int val,int belong){int l,r;split_val(root,val,l,r);root = merge(l,merge(add(val,belong),r));}int find_node_rank(int x){int ans = tr[tr[x].l].siz+1;while(x!=root){int fa=tr[x].fa;if(x==tr[fa].r)ans+=tr[tr[fa].l].siz+1;x=fa;}return ans;}int Delete_node(int x){int rk=find_node_rank(x);int l,r,mid;split_rank(root,rk-1,l,r);split_rank(r,1,mid,r);que.push(mid);root=merge(l,r);return mid;}void dfs_mid(int x,int val,int &l){pushdown(x);if(tr[x].l)dfs_mid(tr[x].l,val,l);if(tr[x].r)dfs_mid(tr[x].r,val,l);tr[x].l=tr[x].r=Null;tr[x].siz=1;tr[x].val-=val;//tag?int L,R;split_val(l,tr[x].val,L,R);l=merge(L,merge(x,R));}void modify(int val){int l,mid,r;split_val(root,val-1,l,r);split_val(r,val+val-1,mid,r);if(r){tr[r].val-=val;tr[r].tag-=val;}if(mid)dfs_mid(mid,val,l);root=merge(l,r);}}treap;
void init()
{vec.clear();rev_vec.clear();for(int i=1;i<=n;i++){pre[i]=0;son[i]=fa[i]=0;}tot=0;num=0;treap.init();
}
int main()
{int T;scanf("%d",&T);while(T--){init();scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)scanf("%d",&p[i]);for(int i=1;i<n;i++){int x,y;scanf("%d%d",&x,&y);add(x,y);add(y,x);}dfs1(1);dfs2(1);for(int i=1;i<=m;i++){scanf("%d%d%d",&q[i].st,&q[i].ed,&q[i].w);LCA(q[i].st,q[i].ed,i);}sort(vec.begin(),vec.end(),cmp);sort(rev_vec.begin(),rev_vec.end(),rev_cmp);for(int i=n,j=0;i>=1;i--){while(j<rev_vec.size() && rev_vec[j].loc == i && rev_vec[j].is_st){treap.insert(q[rev_vec[j].belong].w,rev_vec[j].belong);j++;}treap.modify(p[anti_dfn[i]]);while(j<rev_vec.size() && rev_vec[j].loc == i && !rev_vec[j].is_st){int nd=treap.Delete_node(q[rev_vec[j].belong].tree_node);q[treap.tr[nd].belong].w = treap.tr[nd].val;j++;}}for(int i=1,j=0;i<=n;i++){while(j<vec.size() && vec[j].loc == i && vec[j].is_st){treap.insert(q[vec[j].belong].w,vec[j].belong);j++;}treap.modify(p[anti_dfn[i]]);while(j<vec.size() && vec[j].loc == i && !vec[j].is_st){int nd=treap.Delete_node(q[vec[j].belong].tree_node);q[vec[j].belong].w = treap.tr[nd].val;j++;}}for(int i=1;i<=m;i++){printf("%d\n",q[i].w);}}return 0;
}

【HDU 6973】Bookshop 树剖+平衡树相关推荐

  1. 【HDU 6973】Bookshop (树剖+平衡树)

    Bookshop 这道题让我意识到我根本不会用树剖 参考题解 (感谢聂会 题目链接 题目大意 给出一棵 nnn 个结点的树,每个结点有一个价值为 wiw_iwi​ 的商品 mmm 次询问,每次问如果一 ...

  2. 多校第九场总结,树剖

    http://bestcoder.hdu.edu.cn/blog/ 02 官方题解 由于没有修改操作,一个显然的想法是离线处理所有问题 将询问拆成1-x,1-y,1-LCA(x,y),则处理的问题转化 ...

  3. BZOJ 4817: [Sdoi2017]树点涂色(LCT+树剖+线段树)

    题目描述 Bob有一棵 nn 个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同. 定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色. Bob ...

  4. [SDOI2011]染色 (线段树维护子段问题+树剖)

    题意: 给定一棵 n 个节点的无根树,共有 m 个操作,操作分为两种: 1.将节点 a 到节点 b 的路径上的所有点(包括 a 和 b)都染成颜色 c. 2.询问节点 a 到节点 b 的路径上的颜色段 ...

  5. SP10628 COT - Count on a tree (树剖+可持久化线段树)

    题意: 给定一个包含 N 个结点的树. 树节点从 1 到 N编号..每个节点有一个整数权值. 我们会要求您执行以下操作: u v k : 询问从节点 u 到 节点 v 的路径上的第k小的权值 输入 在 ...

  6. 洛谷P4216 [SCOI2015]情报传递(树剖+主席树)

    传送门 我们可以进行离线处理,把每一个情报员的权值设为它开始收集情报的时间 那么设询问的时间为$t$,就是问路径上有多少个情报员的权值小于等于$t-c-1$ 这个只要用主席树上树就可以解决了,顺便用树 ...

  7. 暑假集训8.10-网络流套树剖套线段树

    题目:dtoj2797旅行商 其实就是裸的网络流套树剖套线段树其实代码不难码 emmmmmm我决定草率的直接上代码,这可能是一条无营养的博客.... #include<bits/stdc++.h ...

  8. BZOJ4034 树上操作(树剖 线段树大模板)

    BZOJ4034 long long 是大坑点 貌似long long 跟int 乘起来会搞事情?... A了这题线段树和树剖的基础OK 嘛 重点过掉的还是线段树区间更新的lazy tag吧 #inc ...

  9. 一棵树的生成树有几颗_次小生成树(树剖,生成树)

    生成树的概念: 在一个无向图中,设顶点数为\(n\),取其中\(n-1\)条边并使所有点相连,所得到的一棵树即为生成树. 最小生成树: 如果还没有接触过生成树的同学,欢迎戳->最小生成树详解 次 ...

最新文章

  1. 业务系统性能问题分析和诊断
  2. 独家 | 送你12个关于数据科学学习的关键提示(附链接)
  3. python培训班那家好-Python培训机构去哪好
  4. Andoird --- 安卓 failed to connect to /192.168.0.135 (port 8080) after 1000ms
  5. 泛函分析——有界线性算子和函数
  6. java基础知识点_零基础学习Java语言,各个阶段需要掌握的知识点
  7. SparkSQL DataFrame进阶篇
  8. 16进制颜色识别和搭配规律
  9. 云图说|将源端MongoDB业务搬迁至华为云DDS的几种方式
  10. Android两个控件叠在一起,如何让被挡住的控件显示出来
  11. Spring IOC中bean标签和管理对象细节
  12. python生活中的小问题_python日常注意小知识集锦
  13. Windows Server 2008 R2远程桌面服务安装配置和授权激活
  14. SPSS决策树和神经网络
  15. 2015校招季,阿里、搜狗、百度、蘑菇街面试总结
  16. Redis安装与使用
  17. 网友关于DTV和IPTV的精彩论述
  18. js高级程序设计(一) —— js简介
  19. 【Python代码基础(符号篇1)】
  20. ROS(机器人操作系统)在国内前景如何?

热门文章

  1. kubernetes 之 pod 调度策略(一)
  2. A Leaky Integrate-and-Fire Laser Neuron for Ultrafast Cognitive Computing 用于超快认知计算的LIF激光神经元
  3. 激光雷达, ToF 与传统雷达的异同
  4. 台式电脑计算机为什么没有EF盘,打开电脑,D、E、F盘不见了,怎么处理?
  5. uos打deb包步骤
  6. 打开计算机跑到桌面外,电脑开机到桌面后关机怎么办
  7. MSP430定时器输出比较不稳定的解决方法
  8. 时薪15美元的ChatGPT外包工人,干的都是苦力活
  9. 系统安全需要考虑哪些东东?
  10. The Illustrated Transformer【译】