萌新刚学OI,树链剖分板子题求调
  • 板块学术版
  • 楼主weiy_
  • 当前回复1
  • 已保存回复1
  • 发布时间2025/1/16 09:16
  • 上次更新2025/1/16 12:57:13
查看原帖
萌新刚学OI,树链剖分板子题求调
946180
weiy_楼主2025/1/16 09:16

RT

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define lc id<<1
#define rc id<<1|1
const ll MAXN=100005;
ll n,m,r,mod,wws[MAXN],wt[MAXN];//题目变量
//------------------------------------------------------------
//链式前向星 
struct edge{
	ll next,to;
}e[MAXN<<1];
ll head[MAXN],cnt;
void add(ll a,ll b){
	e[++cnt].to=b;
	e[cnt].next=head[a];
	head[a]=cnt;
}
//------------------------------------------------------------
//线段树 
struct tree{
	ll l,r,lazy,sum;
}tr[MAXN<<2];
void build(ll id,ll l,ll r){
	tr[id].l=l,tr[id].r=r;
	if(l==r){
		tr[id].sum=wt[l];
		return ;
	}
	ll mid=(l+r)>>1;
	build(lc,l,mid);
	build(rc,mid+1,r);
	tr[id].sum=tr[lc].sum+tr[rc].sum;
	return ;
}
void pushdown(ll id){
	if(tr[id].lazy&&tr[id].l!=tr[id].r){
		tr[lc].lazy+=tr[id].lazy;
		tr[rc].lazy+=tr[id].lazy;
		tr[lc].sum+=tr[id].lazy*(tr[lc].r-tr[lc].l+1);
		tr[rc].sum+=tr[id].lazy*(tr[rc].r-tr[rc].l+1);
		tr[id].lazy=0;
	}
	return ;
}
void add(ll id,ll l,ll r,ll val){//区间修改 
	if(l<=tr[id].l&&tr[id].r<=r){
		tr[id].sum+=val*(tr[id].r-tr[id].l+1);
		tr[id].lazy+=val;
		return ;
	}
	pushdown(id);
	//发现没有被覆盖,就需要继续向下找,考虑到儿子区间可能因为懒标记的存在而没有被修改,所以此处就需要下放懒标记 
	ll mid=(tr[id].l+tr[id].r)/2;
	if(l<=mid) add(lc,l,r,val);
	if(r>mid) add(rc,l,r,val);
	tr[id].sum=tr[lc].sum+tr[rc].sum;
	return ; 
}
ll query(ll id,ll l,ll r){
	if(l<=tr[id].l&&tr[id].r<=r) return tr[id].sum;
	pushdown(id);
	ll mid=(tr[id].l+tr[id].r)/2,val=0;
	if(l<=mid) val+=query(lc,l,r);
	if(r>mid) val+=query(rc,l,r);
	return val;
} 
//------------------------------------------------------------
//树剖核心 
ll dep[MAXN],f[MAXN],son[MAXN],siz[MAXN],top[MAXN];
//dep:深度   f:父亲节点   son:重儿子  siz:子树大小   top:所在链的顶点  
ll dfn[MAXN],num;
//dfn:dfs序   num:计数   wt:将初始值赋值到新编号上,以便进行线段树操作 
void dfs1(ll x,ll fa){//获取 dep、fa、son、siz 
	f[x]=fa;
	siz[x]=1,dep[x]=dep[f[x]]+1;
	for(ll i=head[x];i;i=e[i].next){
		ll y=e[i].to;
		if(y!=f[x]){
			dfs1(y,x);
			siz[x]+=siz[y];
			//迭代更新子树大小 
			if(!son[x]||siz[son[x]]<siz[y]) son[x]=y;
			//没有重儿子,或当前重儿子字数大小不及 y节点,则更新 
		}
	}
}
void dfs2(ll x,ll tv){//获取 top 
	dfn[x]=++num;//dfs序 
	wt[num]=wws[x]; 
	top[x]=tv;//从端点出发 
	if(son[x]) dfs2(son[x],tv);//从重儿子继续连边 
	for(ll i=head[x];i;i=e[i].next){
		ll y=e[i].to;
		if(y==f[x]||y==son[x]) continue;//保证 y不为父节点或重儿子 (重儿子已处理过) 
		dfs2(y,y);//因重链端点定为轻儿子,故从 y更新 (新开链 ) 
	}
	return ;
} 
ll query_range(ll x,ll y){//区间查询 
	ll ans=0;
	while(top[x]!=top[y]){//如果两个点不在一条链上 
		if(dep[top[x]]<dep[top[y]]) swap(x,y);//把 x 改为所在链顶端深度更低那个点 
		ans=(ans+query(1,dfn[top[x]],dfn[x])%mod)%mod;//链上编号连续,区间和为从链顶端到 x 之和 
//		cout<<x<<" "<<y<<" "<<query(1,dfn[top[x]],dfn[x])<<"\n";
		x=f[top[x]];//跳到链顶端的父节点,开始下一轮操作 
	}
	if(dep[x]>dep[y]) swap(x,y);// x 与 y 此时在一条节点上,要确保 x 的编号在 y 的编号前,以便查询 
	ans=(ans+query(1,dfn[x],dfn[y])%mod)%mod;
//	cout<<x<<" "<<y<<" "<<query(1,dfn[x],dfn[y])<<"\n";
	return ans;
}
ll query_son(ll x){//子树查询 
	return query(1,dfn[x],dfn[x]+siz[x]-1);//整棵子树编号连续,和为从第一个节点(根节点)到最后一个节点的区间和 
}
void add_range(ll x,ll y,ll val){//区间修改,原理同上 
	val%=mod;
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		add(1,dfn[top[x]],dfn[x],val);
		x=f[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	add(1,dfn[x],dfn[y],val);
	return ;
} 
void add_son(ll x,ll val){
	add(1,dfn[x],dfn[x]+siz[x]-1,val);
	return ;
} 
//------------------------------------------------------------
signed main(){
//	freopen("P3384_4.in","r",stdin);
//	freopen("P33844.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>n>>m>>r>>mod;
	for(ll i=1;i<=n;i++) cin>>wws[i];
	for(ll i=1;i<n;i++){
		ll x,y;
		cin>>x>>y;
		add(x,y);add(y,x);
	}
	dfs1(r,0);dfs2(r,r);//注意是以 r 为根节点
	build(1,1,n);
//	for(ll i=1;i<=n;i++) cout<<dfn[i]<<" ";
//	cout<<"\n";
//	for(ll i=1;i<=n;i++) cout<<siz[i]<<" ";
//	cout<<"\n";
//	for(ll i=1;i<=n;i++) cout<<f[i]<<" ";
//	cout<<"\n";
//	for(ll i=1;i<=n*2;i++){
//		cout<<i<<":"<<tr[i].l<<" "<<tr[i].r<<"   "<<tr[i].sum<<"\n";
//	}
//	cout<<"\n";
	for(ll i=1;i<=m;i++){
		ll cz,xx,yy,zz;
		cin>>cz;
		if(cz==1){
			cin>>xx>>yy>>zz;
			add_range(xx,yy,zz);
		}
		if(cz==2){
			cin>>xx>>yy;
			cout<<query_range(xx,yy)<<"\n";
		}
		if(cz==3){
			cin>>xx>>zz;
			add_son(xx,zz);
		}
		if(cz==4){
			cin>>xx;
			cout<<query_son(xx)<<"\n";
		}
//		for(ll i=1;i<=n*2-1;i++) cout<<i<<":"<<tr[i].l<<" "<<tr[i].r<<"   "<<tr[i].sum<<"\n"; 
//		cout<<"\n";
	}
	return 0;
2025/1/16 09:16
加载中...