Bookshop
这道题让我意识到我根本不会用树剖
参考题解 (感谢聂会
题目大意
给出一棵 n n n 个结点的树,每个结点有一个价值为 w i w_i wi 的商品
m m m 次询问,每次问如果一个人带着 c c c 块钱,从 x x x 出发到达 y y y 结束,在路上遇到能买的东西就必须买,不能买就继续往前走,最后剩多少钱?(询问独立)
n , m ≤ 1 0 5 w i , c ≤ 1 0 9 n,m≤10^5 \, \, \, \, \, \, w_i,c≤10^9 n,m≤105wi,c≤109
思路
树上区间信息问题,下意识树剖线段树,然鹅这道题的下一个数状态会受到之前数的影响,没有办法做到区间合并,于是我就 gg 了
没有办法区间合并,但是可以按照模拟的思想,按一定顺序枚举点,到 i i i 点的时候就把所有要经过 i i i 点 的 起点或终点 的点 x x x 都进行 c x − w i ( c x > = w i ) c_x-w_i(c_x>=w_i) cx−wi(cx>=wi) ( 最终 c x c_x cx 为 a n s ans ans )
显然 ,可以把问题分解成 x − > l c a x->lca x−>lca ( d f n dfn dfn 递减) 和 l c a − > y lca->y lca−>y ( d f n dfn dfn 递增) 来处理 , 这时候我们要解决枚举到 i i i 点的时候,只有要经过它的起点和终点在我们的数据结构里,这时候 树剖 就大显身手了
比如解决 x − > l c a x->lca x−>lca 时 : 按照 i = n → 1 i=n \rightarrow 1 i=n→1 枚举 d f n [ x ] = i dfn[x]=i dfn[x]=i
这样树剖操作会把 x − > l c a x->lca x−>lca 分解为 l o g ( n ) log(n) log(n) 个区间(树剖保证了每次向上跳的链是一个在 d f n dfn dfn序 上连续的一段) ,在每一个区间 [ l , R ] [l,R] [l,R]中, L L L 点时把起点加入数据结构,再在 R R R 点删去,从而达到预期结果
实现
- 数据结构:维护不小于 k k k 的元素 减去 k k k
-
无旋 t r e a p treap treap 实现 : x ≥ 2 × k x\geq2 \times k x≥2×k 打 t a g tag tag ,同时减去 k k k 不会影响他们之间的排名顺序,同时,其他元素小于 k k k , 相对排名也不影响
-
k ≤ x < 2 × k k \leq x<2 \times k k≤x<2×k 暴力修改 , 均摊 m × l o g ( m ) × l o g ( k ) m \times log(m) \times log(k) m×log(m)×log(k)
- 同时要访问 i d [ x ] id[x] id[x]( x x x 在平衡树中的编号) 并支持删除和加入操作
- 模仿 P5217 贫穷 跳 f a [ ] fa[] fa[] 查出 r a n k rank rank 进行操作
- 哪些点经过区间左右端点??
- 可以把问题离线,跳 L C A LCA LCA 时用 v e c t o r vector vector 放进每一次跳到的端点
Code
# define author XUAN
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+7;
template <typename T> inline void read(T &x)
{
T ch=getchar(),xx=1;x=0;
while(!isdigit(ch)) xx=ch=='-'?-1:xx,ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
x*=xx;
}
template <typename T> void print(T x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);
putchar(x%10+'0');
}
//mt19937 myrand(20051024);
int m,d,n,fa[N],top[N],siz[N],to[N<<1],pre[N<<1],w[N],son[N],seg[N],dfn[N],tot,dep[N],h[N];
void add(int a,int b){++d;pre[d]=h[a];h[a]=d;to[d]=b;return;}
struct Que{
int w,id; // (树上编号)
Que(int _w=0,int _id=0){w=_w;id=_id;return;}
}q[N];
inline void dfs1(int x,int f){
dep[x]=dep[f]+1;
fa[x]=f;siz[x]=1;
for(int i=h[x];i;i=pre[i]){
int y=to[i];if(y==f) continue;
dfs1(y,x);siz[x]+=siz[y];
if(siz[y]>siz[son[x]]) son[x]=y;
}return;
}
inline void dfs2(int x,int f){
dfn[x]=++tot;seg[tot]=x;
if(son[x]){
top[son[x]]=top[x];
dfs2(son[x],x);
}for(int i=h[x];i;i=pre[i]){
int y=to[i];if(y==f || y==son[x]) continue;
top[y]=y;dfs2(y,x);
}return;
}
vector <int> st[N],ed[N],St[N],Ed[N];
inline void LCA(int x,int y,int id){
while(top[x]!=top[y]){
if(dep[top[x]]>dep[top[y]]){
st[dfn[x]].push_back(id);ed[dfn[top[x]]].push_back(id);
x=fa[top[x]];
}else{
St[dfn[top[y]]].push_back(id);Ed[dfn[y]].push_back(id);
y=fa[top[y]];
}
}if(dep[x]>=dep[y]) st[dfn[x]].push_back(id),ed[dfn[y]].push_back(id);
else St[dfn[x]].push_back(id),Ed[dfn[y]].push_back(id);return;
}
class Treap{
private :int rt;Que z[N];int top;
# define lx tr[x].lc
# define rx tr[x].rc
struct Node{
int k,lc,rc,siz,fa,v,tag;
Node(int _k=0,int _lc=0,int _rc=0,int _siz=0,int _fa=0,int _v=0,int _tag=0){
k=_k;lc=_lc;rc=_rc;siz=_siz;fa=_fa;v=_v;tag=_tag;return;
}void fix(int val){tag+=val;v+=val;return;}
}tr[N];
inline void pushup(int x){
tr[x].fa=0;
if(lx) tr[lx].fa=x;if(rx) tr[rx].fa=x;
tr[x].siz=tr[lx].siz+tr[rx].siz+1;return;
}
inline void pushdown(int x){
if(tr[x].tag==0) return;
if(lx) tr[lx].fix(tr[x].tag);
if(rx) tr[rx].fix(tr[x].tag);
tr[x].tag=0;return;
}
inline int New(int id,int val){
tr[id]=Node(rand(),0,0,1,0,val);
return id;
}
inline void split(int x,int key,int &L,int &R){
if(!x){L=R=0;return;}
pushdown(x);
if(tr[lx].siz<key) L=x,split(rx,key-tr[lx].siz-1,rx,R);
else R=x,split(lx,key,L,lx);
pushup(x);return;
}
inline int Find(int x){
int ret=tr[lx].siz+1;
while(x!=rt){
int Fa= tr[x].fa;
if(x==tr[Fa].rc) ret+=tr[tr[Fa].lc].siz+1;
x=Fa;
}
return ret;
}
inline int Rank(int val){
int x=rt,ret=0;
while(x){
pushdown(x);
if(tr[x].v<val) ret+=tr[lx].siz+1,x=rx;
else x=lx;
}return ret+1;
}
inline int merge(int x,int y){
if(!x || !y) return x|y;
if(tr[x].k>tr[y].k){pushdown(x);rx=merge(rx,y);pushup(x);return x;}
pushdown(y);tr[y].lc=merge(x,tr[y].lc);pushup(y);return y;
}
void DFS(int x){
if(!x) return;
pushdown(x);
DFS(lx),DFS(rx);
z[++top]=Que(tr[x].v,x);return;
}
public: inline void Clear(){rt=0;}
inline void Fix(int val){
if(!rt) return;
int l=0,mid=0,r=0;int k=Rank(val);
split(rt,k-1,l,r);rt=r;k=Rank(val+val);
split(rt,k-1,mid,r);top=0;
tr[r].fix(-val);DFS(mid);rt=l;
for(int i=1;i<=top;++i) z[i].w-=val,ins(z[i]);
rt=merge(rt,r);
return;
}
inline void ins(Que a){
int l=0,r=0,k=a.w;k=Rank(k);
split(rt,k-1,l,r);
rt=merge(l,merge(New(a.id,a.w),r));return;
}
inline int del(Que a){
int l=0,r=0,mid=0;int k=Find(a.id);
split(rt,k-1,l,mid);split(mid,1,mid,r);
rt=merge(l,r);return tr[mid].v;
}
}T;
int main()
{
int Case;read(Case);
while(Case--){T.Clear();
read(n);read(m);tot=0;
for(int i=1;i<=n;++i){
st[i].clear();ed[i].clear();
St[i].clear();Ed[i].clear();
h[i]=0,son[i]=0,read(w[i]);
}d=0;
for(int i=1;i<n;++i){
int a,b;read(a),read(b);
add(a,b),add(b,a);
}top[1]=1;dfs1(1,0);dfs2(1,0);
for(int i=1;i<=m;++i){
int a,b;read(a),read(b),read(q[i].w);
LCA(a,b,i);q[i].id=i;
}
for(int i=n;i;--i){ // up
for(int j=0;j<st[i].size();++j)
T.ins(q[st[i][j]]);
T.Fix(w[seg[i]]);
for(int j=0;j<ed[i].size();++j)
q[ed[i][j]].w=T.del(q[ed[i][j]]);
}
for(int i=1;i<=n;++i){ // down
for(int j=0;j<St[i].size();++j)
T.ins(q[St[i][j]]);
T.Fix(w[seg[i]]);
for(int j=0;j<Ed[i].size();++j)
q[Ed[i][j]].w=T.del(q[Ed[i][j]]);
}
for(int i=1;i<=m;++i) print(q[i].w),putchar('\n');
}
return 0;
}