hdu3966树链剖分 分析

这篇博客介绍了如何使用深度优先搜索(DFS)重新编号以保证有序性,并结合线段树来处理有序区间的更新和查询操作。文章详细讲解了DFS的重链和子树连续编号技巧,以及线段树的建立、更新和查询过程。通过实例展示了如何处理单点查询和区间更新,并强调了代码中易错点和优化细节,如位运算的优先级和参数顺序的一致性。
摘要由CSDN通过智能技术生成

/*查找更新等操作  用新编号ne 变为有序的,再用线段树 
ne[] 存的是dfs序编号  保证每个重链和子树都是编号连续的 
有序区间才能用线段树 。所以l,r这些都是新编号,输入里的是旧编号*/
//找了半天读不全的错误 
        移位一定要加括号!!!!
        i<<1|1=3
        (i<<1)+1=3
        i<<1+1=4   
      que的返回值是val 所以是ll 
//mid和m别写混了。因为m一般定义为总量,所以以后中间都用mid!!!
//由于函数的参数比较多,调用函数的次数也比较多,所以参数的顺序尽量有规律 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e4+5;
int m,n,p,size[maxn],deep[maxn],f[maxn],son[maxn];
int cnt,head[maxn],nid[maxn],oid[maxn],ncnt,top[maxn];
ll a[maxn];
struct stree{
	ll value,lazy;
}t[maxn<<2];//线段树范围是4倍 
struct ss{int to,nex;}g[maxn<<1];//g是结构体 没法memset 
void create(int u,int v){
	g[++cnt]={v,head[u]};	head[u]=cnt;
}
//往下走 找重链和重儿子son[]
void dfs1(int x,int fath){
	size[x]=1;deep[x]=deep[fath]+1;
	son[x]=0;f[x]=fath;
	for(int i=head[x];i;i=g[i].nex){
		int v=g[i].to;
		if(v==fath) continue;//不能往上走
		dfs1(v,x);
		size[x]+=size[v];//更新以x为根的树的大小
		if(size[v]>size[son[x]]) son[x]=v;
	}
}
void dfs2(int x,int topx){
	top[x]=topx;//注意 topx不要定义成全局 递归调用时会乱 
	nid[x]=++ncnt;oid[ncnt]=x;
	//因为neid初始化为0,要从1开始 所以++得在前 
    //有重儿子的话,先遍历重链,赋值nid,oid
	if(son[x]) dfs2(son[x],topx);
    //再遍历轻链
	for(int i=head[x];i;i=g[i].nex){
		int v=g[i].to;
		if(v!=f[x]&&v!=son[x]){
			dfs2(v,v);
		}
	}
}


void build(int l=1,int r=n,int rt=1){//建线段树 
	t[rt].lazy=0;
	//建树时给每个点lazy赋初值,所以lazy在外面 。若在l==r时执行 只有叶节点 
	if(l==r){
		t[rt].value=a[oid[l]];//因为a[]下标存的是旧id 
		return;
	}
	int mid=(l+r)>>1;
	build(l,mid,rt<<1); build(mid+1,r,rt<<1|1);
	t[rt].value=t[rt<<1].value+t[rt<<1|1].value;
	//构造都要pushup
}
//lazy value更新都是   加上(减去)用+= 。变为 用= 
void pushdown(int ln,int rn,int rt){
	if(t[rt].lazy){
		t[rt<<1].value+=t[rt].lazy*ln;
		t[rt<<1|1].value+=t[rt].lazy*rn;
		t[rt<<1].lazy+=t[rt].lazy;
		t[rt<<1|1].lazy+=t[rt].lazy;
	}
	t[rt].lazy=0;
}
void update(ll k,int L,int R,int l=1,int r=n,int rt=1){
	if(L>r||R<l) return ;
	if(L<=l&&r<=R){
		t[rt].value+=k*(r-l+1);
		t[rt].lazy+=k;	
		return ;
	}
	int mid=(l+r)>>1;	
	pushdown(mid-l+1,r-mid,rt);
	update(k,L,R,l,mid,rt<<1);
	update(k,L,R,mid+1,r,rt<<1|1);
	//该题是单点查询,所以不用pushup求和  
}
ll que(int k,int l=1,int r=n,int rt=1){
	if(k<l||k>r) return 0;
	//超出范围回0,所以后面可以直接返回左右相加 
	if(l==r) return t[rt].value;
	int mid=(l+r)>>1;
	pushdown(mid-l+1,r-mid,rt);
	//有lazy所以mid后面都要pushdown 才能保证value是正确的 
	return que(k,l,mid,rt<<1)+que(k,mid+1,r,rt<<1|1); 
}
void change(int x,int y,ll k){
	while(top[x]!=top[y]){  
		if(deep[top[x]]<deep[top[y]]) swap(x,y); 
		update(k,nid[top[x]],nid[x]); 
		//更新深度大的那条链,更新完了再向上跳 
        //topx和x是旧编码,要用线段树得用新编码,所以得用nid
		x=f[top[x]];
	}
//前面是while所以一直跳到top相等 即xy在同一重链上(dfs序连续可转为线段树)再更新x到y区间
	if(deep[x]>deep[y]) swap(x,y);; 
	update(k,nid[x],nid[y]); 
}
//change:找到最近公共祖先 并分段update 

int main(){
	int x,y;ll k; 
	while(~scanf("%d%d%d",&n,&m,&p)){ 
		cnt=ncnt=0;
		for(int i=1;i<=n;i++){
			head[i]=0; 
			scanf("%lld",&a[i]);
		}
		for(int i=1;i<=m;i++){
			scanf("%d%d",&x,&y);
			create(x,y);
			create(y,x);
		}
		dfs1(1,0);dfs2(1,1); 
		build();
		for(int i=1;i<=p;i++){
			char ch[10];
			scanf("%s",ch);//%s遇空格结束 
			if(ch[0]=='Q') {
				scanf("%d",&x);
				printf("%lld\n",que(nid[x]));
			} 
			else{
				scanf("%d%d%lld",&x,&y,&k);
				if(ch[0]=='I') change(x,y,k);
				else	change(x,y,-k);
				
			}
		}
	}
	return 0;
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值