动态开点线段树

题目链接

思路

        对于树的线路问题可以用树剖来划分成一段一段的连续区间,但由于只记录这些区间某个信仰的权值,并且只有单点修改,所以我们考虑给每个信仰都开一个线段树,也就是开1e5个线段树(类似主席树的写法,开个root数组来记录每个线段树的根,然后结构体来存那些点和他们的信息,左右儿子),用每个线段树来维护各个信仰的区间和与区间最大值。

#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f;
const int N=1e5+10;
const int mod=1e9+7;
#define fi first
#define se second

int n,q;
int w[N],c[N];
vector<int> e[N];

//树剖
int siz[N],dep[N],newid[N],newc[N],neww[N];
int treecut,son[N],fa[N],top[N];

void dfs(int x,int f){
	siz[x]=1;
	fa[x]=f;
	dep[x]=dep[f]+1;
	for(auto y:e[x]){
		if(y==f)
			continue;
		dfs(y,x);
		siz[x]+=siz[y];
		if(siz[y]>siz[son[x]])
			son[x]=y;
	}
}

void dfs(int x,int f,int id){
	newid[x]=++treecut;
	top[x]=id;//是用原节点x来记录top,因为top是在树在lca的,树上的信息都记录的x,treecut已经转化成链了 
	newc[treecut]=c[x];//这些都是维护新下标的信息 
	neww[treecut]=w[x];
	if(son[x]){
		dfs(son[x],x,id);//仍然是id,表示这条重链的头都是id 
	}
	for(auto y:e[x]){
		if(y==f||y==son[x]) //重儿子不要再进 
			continue;
		dfs(y,x,y);
	}
}

//线段树 
int root[N],treecnt;
struct node{
	int l,r,maxx,sum;
}tree[N*20];

void pushup(int l,int r,int &k){
	int ls=tree[k].l,rs=tree[k].r;
	tree[k].maxx=max(tree[ls].maxx,tree[rs].maxx);
	tree[k].sum=tree[ls].sum+tree[rs].sum;
}

void modify(int l,int r,int &k,int x,int value){
	if(!k) k=++treecnt;//k是引用,为0表示为NULL,要创节点 
	if(l==r){
		tree[k].maxx=tree[k].sum=value;
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) modify(l,mid,tree[k].l,x,value);
	else modify(mid+1,r,tree[k].r,x,value);
	pushup(l,r,k);
}

int querysum(int l,int r,int &k,int x,int y){
	if(x<=l&&y>=r)
		return tree[k].sum;
	int mid=(l+r)>>1;
	int res=0;
	if(x<=mid) res+=querysum(l,mid,tree[k].l,x,y);
	if(y>mid) res+=querysum(mid+1,r,tree[k].r,x,y);
	return res;
}

int querymax(int l,int r,int &k,int x,int y){
	if(x<=l&&y>=r)
		return tree[k].maxx;
	int mid=(l+r)>>1;
	int res=0;
	if(x<=mid) res=max(res,querymax(l,mid,tree[k].l,x,y));
	if(y>mid) res=max(res,querymax(mid+1,r,tree[k].r,x,y));
	return res;
}

//lca
int cc;//表示lca的子树是哪个信仰子树 
int lcasum(int a,int b){
	int res=0;
	while(top[a]!=top[b]){//不在同一重链 
		if(dep[top[a]]<dep[top[b]])//保证a所在链的dep要大 
			swap(a,b);
		res+=querysum(1,n,root[cc],newid[top[a]],newid[a]);
		a=fa[top[a]];//要fa来跳出这条链 
	}
	if(newid[a]>newid[b])
		swap(a,b);
	res+=querysum(1,n,root[cc],newid[a],newid[b]);
	return res;
}

int lcamax(int a,int b){
	int res=0;
	while(top[a]!=top[b]){
		if(dep[top[a]]<dep[top[b]])
			swap(a,b);
		res=max(res,querymax(1,n,root[cc],newid[top[a]],newid[a]));
		a=fa[top[a]];
	}
	if(newid[a]>newid[b])
		swap(a,b);
	res=max(res,querymax(1,n,root[cc],newid[a],newid[b]));
	return res;
}

void solve(){
	cin>>n>>q;
	for(int i=1;i<=n;i++){
		cin>>w[i]>>c[i];
	}
	for(int i=1;i<n;i++){
		int a,b;cin>>a>>b;
		e[a].push_back(b);
		e[b].push_back(a);
	}
	
	dfs(1,0);
	dfs(1,0,1);
	for(int i=1;i<=n;i++){//全用新的了... 
		modify(1,n,root[newc[newid[i]]],newid[i],neww[newid[i]]);
	}
	while(q--){	
		string s;
		cin>>s;
		int x,y;
		cin>>x>>y;
		cc=newc[newid[x]];//信仰 
		if(s=="CC"){
			int t=querysum(1,n,root[newc[newid[x]]],newid[x],newid[x]);
			modify(1,n,root[newc[newid[x]]],newid[x],0);
			modify(1,n,root[y],newid[x],t);
			newc[newid[x]]=y;//记得修改信仰 
		}
		else if(s=="CW"){
			modify(1,n,root[newc[newid[x]]],newid[x],y);
			neww[newid[x]]=y;
		}
		else if(s=="QS"){
			cout<<lcasum(x,y)<<endl;
		}
		else{
			cout<<lcamax(x,y)<<endl;
		}
	}
}
signed  main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t=1;
	//cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值