[树链剖分,线段树]P2146

本文介绍如何利用线段树结构在大规模数据结构中实现installinstallinstall和uninstalluninstalluninstall操作的高效查询与修改,关注于对节点路径上操作的影响及更新节点计数,适用于n,q≤10^5的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(简化题意)要对题目中的 installinstallinstalluninstalluninstalluninstall 理解清楚, installinstallinstall 是对该节点到 rootrootroot 路径上的所有点取1, uninstalluninstalluninstall 是对该节点所在子树取0。每次操作求出更改的节点个数。n,q≤105n,q\leq 10^5n,q105


在执行线段树的查找同时进行修改,写好模板一切都好说。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,m;
ll in(){
	ll x=0,f=1;
	char c;
	do{
		c=getchar();
		if(c=='-')
			f=-1;
	}while(c>'9' || c<'0');
	while(c>='0' && c<='9'){
		x=(x<<3)+(x<<1)+c-'0';
		c=getchar();
	}
	return x*f;
}

struct ST{
	struct ST_node{
		int l,r,sum,tag;
	}nd[400005];
	#define ls(p) p<<1
	#define rs(p) p<<1|1
	inline void push_up(int p){
		nd[p].sum=nd[ls(p)].sum+nd[rs(p)].sum;
	}
	inline void add_tag(int p,int k){
		nd[p].tag=k;
		nd[p].sum=(k-1)*(nd[p].r-nd[p].l+1);
	}
	void push_down(int p){
		if(nd[p].tag){
			add_tag(ls(p),nd[p].tag);
			add_tag(rs(p),nd[p].tag);
			nd[p].tag=0;
		}
	}
	void build(int l,int r,int p){
		nd[p].l=l;
		nd[p].r=r;
		if(l==r)
			return;
		int mid=(l+r)>>1;
		build(l,mid,ls(p));
		build(mid+1,r,rs(p));
		push_up(p);
	}
	ll install(int ql,int qr,int p,int k){
		int l=nd[p].l;
		int r=nd[p].r;
		int ret=0;
		if(ql<=l && r<=qr){
			ret=nd[p].sum;
			if(k==2)
				ret=nd[p].r-nd[p].l+1-ret;
			add_tag(p,k);
			return ret;
		}
		push_down(p);
		int mid=(l+r)>>1;
		if(ql<=mid)
			ret+=install(ql,qr,ls(p),k);
		if(qr>mid)
			ret+=install(ql,qr,rs(p),k);
		push_up(p);
		return ret;
	}
}S;

struct Tree{
	struct Tree_node{
		int fa,sz,to,son,top,dep;
		vector <int> v;
	}nd[100005];
	int cnt;
	void DFS1(int x){
		nd[x].sz=1;
		nd[x].dep=nd[nd[x].fa].dep+1;
		int mx=-1;
		for(int i=0;i<nd[x].v.size();++i){
			int y=nd[x].v[i];
			DFS1(y);
			nd[x].sz+=nd[y].sz;
			if(nd[y].sz>mx){
				mx=nd[y].sz;
				nd[x].son=y;
			}
		}
	}
	void DFS2(int x,int top){
		nd[x].top=top;
		cnt++;
		nd[x].to=cnt;
		if(nd[x].son)
			DFS2(nd[x].son,top);
		for(int i=0;i<nd[x].v.size();++i){
			int y=nd[x].v[i];
			if(y==nd[x].son)
				continue;
			DFS2(y,y);
		}
	}
	void operate(int op,int x){
		if(op==2){
			int y=0;
			ll ret=0;
			while(nd[x].top!=nd[y].top){
				if(nd[nd[x].top].dep<nd[nd[x].top].dep)
					swap(x,y);
				ret+=S.install(nd[nd[x].top].to,nd[x].to,1,op);
				x=nd[nd[x].top].fa;
			}
			if(nd[x].dep>nd[y].dep)
				swap(x,y);
			ret+=S.install(nd[x].to,nd[y].to,1,op);
			printf("%lld\n",ret);
		}
		else{
			printf("%lld\n",S.install(nd[x].to,nd[x].to+nd[x].sz-1,1,op));
		}
	}
}T;

int main(){
	n=in();
	for(int i=1;i<n;++i){
		T.nd[i].fa=in();
		T.nd[T.nd[i].fa].v.push_back(i);
	}
	T.DFS1(0);
	T.DFS2(0,0);
	S.build(1,n,1);
	m=in();
	while(m){
		m--;
		char c;
		do{
			c=getchar();
		}while(c!='i' && c!='u');
		int x=in();
		if(c=='i'){
			T.operate(2,x);
		}
		else{
			T.operate(1,x);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值