1. 程式人生 > 其它 >P3899 [湖南集訓]談笑風生 - 線段樹合併

P3899 [湖南集訓]談笑風生 - 線段樹合併

題解

對於每個點,以深度為下標記錄答案。

如果線段樹合併想線上,Merge 函式需要這樣寫:

int Merge(int p,int q){
	if(!p||!q) return p^q;
	int res=Newnode(t[p].l,t[p].r,t[p].v+t[q].v);
	ls(res)=Merge(ls(p),ls(q)),rs(res)=Merge(rs(p),rs(q));
	return res;
}

\(p\)\(q\) 為空時,直接返回另一個。這保證了線段樹合併的時空複雜度,但也使得修改父親節點的線段樹時可能修改到子節點。

兩種解決方案:

  • 將修改放到合併前面;
  • 將修改可持久化。這樣,每次修改一定不會影響原樹。合併時只合並最後一個版本即可。
程式碼
#include <cstdio>
#include <cstring>
#include <cctype>
#include <vector>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
template<typename T> void Read(T &x){
	x=0;int _f=1;
	char ch=getchar();
	while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();
	while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
	x=x*_f;
}
template<typename T,typename... Args> void Read(T &x,Args& ...others){
	Read(x);Read(others...);
}
typedef long long ll;
const int Inf=0x3f3f3f3f,N=3e5+5;
int n,q;
vector<int> G[N];
int dep[N],siz[N];
void Dfs(int u,int fa){
	dep[u]=dep[fa]+1,siz[u]=1;
	for(int v:G[u]){
		if(v==fa) continue;
		Dfs(v,u);siz[u]+=siz[v];
	}
}
#define ls(xx) t[xx].ls
#define rs(xx) t[xx].rs
struct Node{
	int l,r,ls,rs;ll v;
}t[20000000];
int tot=0;
int Newnode(int l,int r,ll v=0){
	t[++tot]=Node{l,r,0,0,v};
	return tot;
}
void Pushup(int p){t[p].v=t[ls(p)].v+t[rs(p)].v;}
void Add(int p,int pos,ll x){
	if(t[p].l==t[p].r){
		t[p].v+=x;return;
	}
	int mid=(t[p].l+t[p].r)>>1;
	if(pos<=mid){
		if(!ls(p)) ls(p)=Newnode(t[p].l,mid);
		Add(ls(p),pos,x);
	}else{
		if(!rs(p)) rs(p)=Newnode(mid+1,t[p].r);
		Add(rs(p),pos,x);
	}
	Pushup(p);
}
ll Query(int p,int l,int r){
	if(!p||r<t[p].l||l>t[p].r) return 0;
	if(l<=t[p].l&&t[p].r<=r) return t[p].v;
	return Query(ls(p),l,r)+Query(rs(p),l,r);
}
int Merge(int p,int q){
	if(!p||!q) return p^q;
	int res=Newnode(t[p].l,t[p].r,t[p].v+t[q].v);
	ls(res)=Merge(ls(p),ls(q)),rs(res)=Merge(rs(p),rs(q));
	return res;
}
int root[N];
void Work(int u,int fa){
	root[u]=Newnode(1,n);
	Add(root[u],dep[u],siz[u]-1);
	for(int v:G[u]){
		if(v==fa) continue;
		Work(v,u);
		root[u]=Merge(root[u],root[v]);
	}
}
int main(){
	Read(n,q);
	int u,v;
	For(i,1,n-1){
		Read(u,v);
		G[u].push_back(v),G[v].push_back(u);
	}
	Dfs(1,0);
	Work(1,0);
	while(q--){
		int p,k;Read(p,k);
		printf("%lld\n",1LL*min(dep[p]-1,k)*(siz[p]-1)+Query(root[p],dep[p]+1,min(dep[p]+k,n)));
	}
	return 0;
}