QUERY - Observing the Tree

一棵树支持三个操作。

①给链加等差数列

②查询链上的和

③回到第x次修改后

强制在线。

树剖+主席树。做完了。

注意这里要用到标记永久化。也就是说标记不下传,询问时直接拿来加。只有满足结合律才可以标记永久化。

典型的代码题。嘴巴可做,手写崩溃。

数组往死里开。反正我的代码,三千万太小、六千万开不了、四千万能过!

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define per(i,j,k) for(i=j;i>=k;--i)
#define sqr(x) ((x)*(x))
#define G getchar()
#define LL long long
#define pll pair<LL,LL>
#define mkp make_pair
#define X first
#define Y second
#define N 200005
#define NN 40000000
#define inf 1000000000000000LL
int n;LL ans;
int he[N],ne[N<<1],to[N<<1],tot;
int dep[N],sz[N],son[N],dad[N],pre[N],dfn[N],nfd[N],cnt;
int m,las,now,wz[N],rt[NN],id,lc[NN],rc[NN];LL la[NN],ld[NN],sum[NN];
int read(){
	int x=0;char ch=G;
	for(;ch<48||ch>57;ch=G);
	for(;ch>47&&ch<58;ch=G)x=x*10+ch-48;
	return x;
}
char readc(){
	char ch=G;
	while(ch<97||ch>122)ch=G;
	return ch;
}
void add(int x,int y){
	to[++tot]=y;ne[tot]=he[x];he[x]=tot;
}
void DFS1(int x){
	int y,i;dep[x]=dep[pre[x]]+1;sz[x]=1;
	for(i=he[x];i;i=ne[i])if((y=to[i])!=pre[x]){
		pre[y]=x;DFS1(y);
		if(sz[y]>sz[son[x]])son[x]=y;sz[x]+=sz[y];
	}
}
void DFS2(int x){
	int y,i;dad[x]=son[pre[x]]==x?dad[pre[x]]:x;nfd[dfn[x]=++cnt]=x;
	if(son[x])DFS2(son[x]);
	for(i=he[x];i;i=ne[i])if(!dfn[y=to[i]])
		DFS2(y);
}
int lca(int x,int y){
	int fx=dad[x],fy=dad[y];
	while(fx!=fy)
		if(dep[fx]>dep[fy])fx=dad[x=pre[fx]];
		else fy=dad[y=pre[fy]];
	return dep[x]<dep[y]?x:y;
}
LL cal(LL a,LL b,LL x){
	return (a+b)*x>>1;
}
void ins(int L,int R,LL a,LL d,int l,int r,int&num,int old){
	la[num=++id]=la[old];ld[num]=ld[old];sum[num]=sum[old];
	if(L<=l&&r<=R){
		la[num]+=a+d*(l-L);ld[num]+=d;sum[num]+=cal(a+d*(l-L),a+d*(r-L),r-l+1);
		lc[num]=lc[old];rc[num]=rc[old];
		return;
	}
	int mid=l+r>>1;
	if(L<=mid)ins(L,R,a,d,l,mid,lc[num],lc[old]);
	else lc[num]=lc[old];
	if(R>mid)ins(L,R,a,d,mid+1,r,rc[num],rc[old]);
	else rc[num]=rc[old];
	sum[num]=sum[lc[num]]+sum[rc[num]]+cal(la[num],la[num]+ld[num]*(r-l),r-l+1);
}
void query(int L,int R,int l,int r,int num){
	if(!num)return;
	if(l==L&&R==r){
		ans+=sum[num];return;
	}
	ans+=cal(la[num]+ld[num]*(L-l),la[num]+ld[num]*(R-l),R-L+1);
	int mid=l+r>>1;
	if(L<=mid)query(L,min(R,mid),l,mid,lc[num]);
	if(R>mid)query(max(L,mid+1),R,mid+1,r,rc[num]);
}
int main(){
	int i,Q,x,y,z,fx,fy;char ch;LL a,d;
	n=read();Q=read();
	rep(i,2,n){
		x=read();y=read();add(x,y);add(y,x);
	}
	DFS1(1);DFS2(1);
	while(Q--){
		ch=readc();
		if(ch=='c'){
			x=(read()+ans)%n+1;y=(read()+ans)%n+1;a=read();d=read();
			z=lca(x,y);a-=d;
			for(fx=dad[x];dep[fx]>dep[z];fx=dad[x=pre[fx]]){
				a+=d*(dfn[x]-dfn[fx]+1);
				ins(dfn[fx],dfn[x],a,-d,1,n,rt[++now],rt[las]);las=now;
			}
			if(dfn[z]<dfn[x]){
				a+=d*(dfn[x]-dfn[z]);
				ins(dfn[z]+1,dfn[x],a,-d,1,n,rt[++now],rt[las]);las=now;
			}
			a+=d*(dep[y]-dep[z]+2);
			for(fy=dad[y];dep[fy]>dep[z];fy=dad[y=pre[fy]]){
				a-=d*(dfn[y]-dfn[fy]+1);
				ins(dfn[fy],dfn[y],a,d,1,n,rt[++now],rt[las]);las=now;
			}
			a-=d*(dfn[y]-dfn[z]+1);
			ins(dfn[z],dfn[y],a,d,1,n,rt[++now],rt[las]);las=now;
			wz[++m]=now;
		}
		else if(ch=='q'){
			x=(read()+ans)%n+1;y=(read()+ans)%n+1;
			ans=0;z=lca(x,y);
			for(fx=dad[x];dep[fx]>dep[z];fx=dad[x=pre[fx]])
				query(dfn[fx],dfn[x],1,n,rt[las]);
			if(dfn[z]<dfn[x])query(dfn[z]+1,dfn[x],1,n,rt[las]);
			for(fy=dad[y];dep[fy]>dep[z];fy=dad[y=pre[fy]])
				query(dfn[fy],dfn[y],1,n,rt[las]);
			query(dfn[z],dfn[y],1,n,rt[las]);
			printf("%lld\n",ans);
		}
		else las=wz[(read()+ans)%(m+1)];
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值