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

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,m105wi,c109

思路

树上区间信息问题,下意识树剖线段树,然鹅这道题的下一个数状态会受到之前数的影响,没有办法做到区间合并,于是我就 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) cxwi(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=n1 枚举 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 点删去,从而达到预期结果

实现

  1. 数据结构:维护不小于 k k k 的元素 减去 k k k
  • 无旋 t r e a p treap treap 实现 : x ≥ 2 × k x\geq2 \times k x2×k t a g tag tag ,同时减去 k k k 不会影响他们之间的排名顺序,同时,其他元素小于 k k k , 相对排名也不影响

  • k ≤ x < 2 × k k \leq x<2 \times k kx<2×k 暴力修改 , 均摊 m × l o g ( m ) × l o g ( k ) m \times log(m) \times log(k) m×log(m)×log(k)

  1. 同时要访问 i d [ x ] id[x] id[x]( x x x 在平衡树中的编号) 并支持删除和加入操作
  • 模仿 P5217 贫穷 f a [ ] fa[] fa[] 查出 r a n k rank rank 进行操作
  1. 哪些点经过区间左右端点??
  • 可以把问题离线,跳 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;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值