【GDOI2016】疯狂动物城(树链剖分+可持久化线段树)

码农题…
调了我三个晚上…
看来我的代码能力还是太弱了…
首先我们不难发现在u到v这条链的答案为 ∑ i = 1 n ( n − i ) ( n − i + 1 ) a i 2 \sum_{i=1}^n\frac{(n-i)(n-i+1)a_i}{2} i=1n2(ni)(ni+1)ai
然后把它拆开可以得到答案为 ∑ i = 1 n ( n + 1 ) n a i − ( 2 n + 1 ) i a i + i 2 a i \sum_{i=1}^n(n+1)na_i-(2n+1)ia_i+i^2a_i i=1n(n+1)nai(2n+1)iai+i2ai
对链进行操作自然就是树剖啦
对于第一部分就是普通的树剖
第二部分,我们对线段树的每一个节点维护 s u m r = ∑ i = 1 n i a i sumr=\sum_{i=1}^nia_i sumr=i=1niai s u m l = ∑ i = 1 n ( n − i + 1 ) a i suml=\sum_{i=1}^n(n-i+1)a_i suml=i=1n(ni+1)ai
信息合并: s u m r [ o ] = s u m r [ l c h [ o ] ] + s u m r [ r c h [ o ] ] + s u m [ r c h [ o ] ] ∗ s i z [ l c h [ o ] ] sumr[o]=sumr[lch[o]]+sumr[rch[o]]+sum[rch[o]]*siz[lch[o]] sumr[o]=sumr[lch[o]]+sumr[rch[o]]+sum[rch[o]]siz[lch[o]]
s u m l suml suml同理
其中 s u m sum sum表示当前子树所表示的区间的权值和
懒标记就加上 t a g [ o ] ∗ s i z [ o ] ∗ ( s i z [ o ] + 1 ) 2 \frac{tag[o]*siz[o]*(siz[o]+1)}{2} 2tag[o]siz[o](siz[o]+1)
第三部分,我们对线段树的每一个节点维护 s s u m r = ∑ i = 1 n i 2 a i ssumr=\sum_{i=1}^ni^2a_i ssumr=i=1ni2ai s s u m l = ∑ i = 1 n ( n − i + 1 ) 2 a i ssuml=\sum_{i=1}^n(n-i+1)^2a_i ssuml=i=1n(ni+1)2ai
信息合并: s s u m r [ o ] = s s u m r [ l c h [ o ] ] + s s u m [ r c h [ o ] ] + s u m [ r c h [ o ] ] ∗ s i z [ l c h [ o ] ] 2 + 2 s u m r [ r c h [ o ] ] ∗ s i z [ l c h [ o ] ] ssumr[o]=ssumr[lch[o]]+ssum[rch[o]]+sum[rch[o]]*siz[lch[o]]^2+2sumr[rch[o]]*siz[lch[o]] ssumr[o]=ssumr[lch[o]]+ssum[rch[o]]+sum[rch[o]]siz[lch[o]]2+2sumr[rch[o]]siz[lch[o]]
s u m l suml suml同理
懒标记就加上 t a g [ o ] ∗ s i z [ o ] ∗ ( s i z [ o ] + 1 ) ∗ ( 2 s i z [ o ] + 1 ) 6 \frac{tag[o]*siz[o]*(siz[o]+1)*(2siz[o]+1)}{6} 6tag[o]siz[o](siz[o]+1)(2siz[o]+1)
树剖的时候的信息合并同理。
再加上一个持久化就好了。
为了避免爆空间,加个标记永久化就好了。
然后就是考验代码能力的时候了!!!!!!!!
如果有误在评论区吼一声哦!
代码:

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll mod=20160501,inv2=10080251;
const int N=1e5+10;
int n,m,tot,cnt,tcnt,bcnt,now,lastans,head[N],to[N<<1],nxt[N<<1],f[N],dep[N],tp[N],son[N],siz[N],rnk[N],dfn[N],val[N],rt[N],ch[N*100][2],idx[N*100];
ll sum[N*100][5],tag[N*100];
void add_edge(int u,int v){
	nxt[++tot]=head[u];
	to[tot]=v;
	head[u]=tot;
	return;
}
void maintain(int o,int l,int r){
	int mid=(l+r)>>1;
	sum[o][0]=(sum[ch[o][0]][0]+sum[ch[o][1]][0]+sum[ch[o][1]][2]*(mid-l+1)+1ll*(r-l+2)*(r-l+1)/2%mod*tag[o])%mod;
	sum[o][1]=(sum[ch[o][0]][1]+sum[ch[o][1]][1]+sum[ch[o][0]][2]*(r-mid)+1ll*(r-l+2)*(r-l+1)/2%mod*tag[o])%mod;
	sum[o][2]=(sum[ch[o][0]][2]+sum[ch[o][1]][2]+tag[o]*(r-l+1))%mod;
	sum[o][3]=(sum[ch[o][0]][3]+sum[ch[o][1]][3]+sum[ch[o][1]][2]*(mid-l+1)*(mid-l+1)+sum[ch[o][1]][0]*2*(mid-l+1)+1ll*(r-l+1)*(r-l+2)*(2*r-2*l+3)/6%mod*tag[o])%mod;
	sum[o][4]=(sum[ch[o][0]][4]+sum[ch[o][1]][4]+sum[ch[o][0]][2]*(r-mid)%mod*(r-mid)+sum[ch[o][0]][1]*2*(r-mid)+1ll*(r-l+1)*(r-l+2)*(2*r-2*l+3)/6%mod*tag[o])%mod;
	return;
}
void build(int &o,int l,int r){
	o=++tcnt;
	if(l==r){
		sum[o][0]=sum[o][1]=sum[o][2]=sum[o][3]=sum[o][4]=val[rnk[l]];
		return;
	}
	int mid=(l+r)>>1;
	build(ch[o][0],l,mid);
	build(ch[o][1],mid+1,r);
	maintain(o,l,r);
	return;
}
void dfs1(int u){
	siz[u]=1;
	for(int i=head[u];~i;i=nxt[i]){
		int v=to[i];
		if(v==f[u])
			continue;
		f[v]=u;
		dep[v]=dep[u]+1;
		dfs1(v);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])
			son[u]=v;
	}
	return;
}
void dfs2(int u,int tpx){
	rnk[dfn[u]=++cnt]=u;
	tp[u]=tpx;
	if(!son[u])
		return;
	dfs2(son[u],tpx);
	for(int i=head[u];~i;i=nxt[i]){
		int v=to[i];
		if(v!=f[u]&&v!=son[u])
			dfs2(v,v);
	}
	return;
}
ll query(int o,int l,int r,int L,int R,int k){
	if(!o)
		return 0;
	if(L==l&&r==R)
		return sum[o][k];
	int mid=(l+r)>>1;
	if(R<=mid)
		return (query(ch[o][0],l,mid,L,R,k)+tag[o]*(k==2?(R-L+1):(k<2?1ll*(R-L+1)*(R-L+2)/2%mod:1ll*(R-L+1)*(R-L+2)*(2*R-2*L+3)/6%mod)))%mod;
	else if(L>mid)
		return (query(ch[o][1],mid+1,r,L,R,k)+tag[o]*(k==2?(R-L+1):(k<2?1ll*(R-L+1)*(R-L+2)/2%mod:1ll*(R-L+1)*(R-L+2)*(2*R-2*L+3)/6%mod)))%mod;
	if(!k)
		return (query(ch[o][0],l,mid,L,mid,k)+query(ch[o][1],mid+1,r,mid+1,R,2)*(mid-L+1)+query(ch[o][1],mid+1,r,mid+1,R,k)+1ll*(R-L+1)*(R-L+2)/2%mod*tag[o])%mod;
	if(k==1)
		return (query(ch[o][0],l,mid,L,mid,k)+query(ch[o][0],l,mid,L,mid,2)*(R-mid)+query(ch[o][1],mid+1,r,mid+1,R,k)+1ll*(R-L+1)*(R-L+2)/2%mod*tag[o])%mod;
	if(k==3)
		return (query(ch[o][0],l,mid,L,mid,k)+query(ch[o][1],mid+1,r,mid+1,R,k)+query(ch[o][1],mid+1,r,mid+1,R,2)*(mid-L+1)%mod*(mid-L+1)+query(ch[o][1],mid+1,r,mid+1,R,0)*2*(mid-L+1)+1ll*(R-L+1)*(R-L+2)*(2*R-2*L+3)/6%mod*tag[o])%mod;
	if(k==4)
		return (query(ch[o][0],l,mid,L,mid,k)+query(ch[o][1],mid+1,r,mid+1,R,k)+query(ch[o][0],l,mid,L,mid,2)*(R-mid)%mod*(R-mid)+query(ch[o][0],l,mid,L,mid,1)*2*(R-mid)+1ll*(R-L+1)*(R-L+2)*(2*R-2*L+3)/6%mod*tag[o])%mod;
	return (query(ch[o][0],l,mid,L,mid,k)+query(ch[o][1],mid+1,r,mid+1,R,k)+tag[o]*(R-L+1))%mod;
}
void update(int &o,int p,int l,int r,int L,int R,ll v){
	if(idx[o]!=bcnt){
		o=++tcnt;
		idx[o]=bcnt;
		ch[o][0]=ch[p][0];
		ch[o][1]=ch[p][1];
		tag[o]=tag[p];
		sum[o][0]=sum[p][0];
		sum[o][1]=sum[p][1];
		sum[o][2]=sum[p][2];
		sum[o][3]=sum[p][3];
		sum[o][4]=sum[p][4];
	}
	if(L<=l&&r<=R){
		tag[o]=(tag[o]+v)%mod;
		sum[o][0]=(sum[o][0]+1ll*(r-l+1)*(r-l+2)/2%mod*v)%mod;
		sum[o][1]=(sum[o][1]+1ll*(r-l+1)*(r-l+2)/2%mod*v)%mod;
		sum[o][2]=(sum[o][2]+v*(r-l+1))%mod;
		sum[o][3]=(sum[o][3]+1ll*(r-l+1)*(r-l+2)*(2*r-2*l+3)/6%mod*v)%mod;
		sum[o][4]=(sum[o][4]+1ll*(r-l+1)*(r-l+2)*(2*r-2*l+3)/6%mod*v)%mod;
		return;
	}
	int mid=(l+r)>>1;
	if(L<=mid)
		update(ch[o][0],ch[p][0],l,mid,L,R,v);
	if(R>mid)
		update(ch[o][1],ch[p][1],mid+1,r,L,R,v);
	maintain(o,l,r);
	return;
}
ll Query(int x,int y){
	ll ret1=0,ret2=0,ret3=0,ret4=0,ret5=0,sum2=0;
	int siz1=0,siz3=0;
	while(tp[x]!=tp[y]){
		if(dep[tp[x]]>dep[tp[y]]){
			const ll s=query(rt[now],1,n,dfn[tp[x]],dfn[x],2),ss=query(rt[now],1,n,dfn[tp[x]],dfn[x],1);
			ret4=(ret4+query(rt[now],1,n,dfn[tp[x]],dfn[x],4)+s*siz1%mod*siz1+ss*2*siz1)%mod;
			ret1=(ret1+ss+s*siz1)%mod;
			ret3=(ret3+s)%mod;
			siz1+=dfn[x]-dfn[tp[x]]+1;
			siz3+=dfn[x]-dfn[tp[x]]+1;
			x=f[tp[x]];
		}
		else{
			const ll s=query(rt[now],1,n,dfn[tp[y]],dfn[y],2);
			ret5=((ret5+query(rt[now],1,n,dfn[tp[y]],dfn[y],3)+sum2*(dfn[y]-dfn[tp[y]]+1)%mod*(dfn[y]-dfn[tp[y]]+1)+ret2*2*(dfn[y]-dfn[tp[y]]+1)))%mod;
			ret2=(ret2+(dfn[y]-dfn[tp[y]]+1)*sum2+query(rt[now],1,n,dfn[tp[y]],dfn[y],0))%mod;
			ret3=(ret3+s)%mod;
			sum2=(sum2+s)%mod;
			siz3+=dfn[y]-dfn[tp[y]]+1;
			y=f[tp[y]];
		}
	}
	if(dep[x]<dep[y]){
		const ll s=query(rt[now],1,n,dfn[x],dfn[y],2);
		ret5=((ret5+query(rt[now],1,n,dfn[x],dfn[y],3)+sum2*(dfn[y]-dfn[x]+1)%mod*(dfn[y]-dfn[x]+1)+ret2*2*(dfn[y]-dfn[x]+1)))%mod;
		ret2=(ret2+(dfn[y]-dfn[x]+1)*sum2+query(rt[now],1,n,dfn[x],dfn[y],0))%mod;
		ret3=(ret3+s)%mod;
		sum2=(sum2+s)%mod;
		siz3+=dfn[y]-dfn[x]+1;
	}
	else{
		const ll s=query(rt[now],1,n,dfn[y],dfn[x],2),ss=query(rt[now],1,n,dfn[y],dfn[x],1);
		ret4=(ret4+query(rt[now],1,n,dfn[y],dfn[x],4)+s*siz1%mod*siz1+ss*2*siz1)%mod;
		ret1=(ret1+ss+s*siz1)%mod;
		ret3=(ret3+s)%mod;
		siz1+=dfn[x]-dfn[y]+1;
		siz3+=dfn[x]-dfn[y]+1;
	}
	ret4=(ret4+ret5+sum2*siz1%mod*siz1+ret2*2*siz1)%mod;
	ret1=(ret1+sum2*siz1+ret2)%mod;
	return ((1ll*siz3*(siz3+1)%mod*ret3-(2*siz3+1)*ret1+ret4)%mod*inv2%mod+mod)%mod;
}
void Update(int x,int y,int v){
	++bcnt;
	while(tp[x]!=tp[y]){
		if(dep[tp[x]]<dep[tp[y]])
			swap(x,y);
		update(rt[bcnt],rt[now],1,n,dfn[tp[x]],dfn[x],v);
		x=f[tp[x]];
	}
	if(dep[x]>dep[y])
		swap(x,y);
	update(rt[bcnt],rt[now],1,n,dfn[x],dfn[y],v);
	now=bcnt;
	return;
}
int rd(){
	int x=0;
	char c;
	do c=getchar();
	while(!isdigit(c));
	do{
		x=(x<<1)+(x<<3)+(c^48);
		c=getchar();
	}while(isdigit(c));
	return x;
}
void InitInput(){
	memset(head,-1,sizeof(head));
	n=rd();
	m=rd();
	for(int i=1;i<n;i++){
		int u=rd(),v=rd();
		add_edge(u,v);
		add_edge(v,u);
	}
	for(int i=1;i<=n;i++)
		val[i]=rd();
	dfs1(1);
	dfs2(1,1);
	build(rt[0],1,n);
	return;
}
void Ask(){
	while(m--){
		int opt=rd();
		if(opt==1){
			int x=rd()^lastans,y=rd()^lastans,delta=rd();
			Update(x,y,delta);
		}
		else if(opt==2){
			int x=rd()^lastans,y=rd()^lastans;
			lastans=Query(x,y);
			printf("%d\n",lastans);
		}
		else{
			int x=rd()^lastans;
			now=x;
		}
	}
	return;
}
int main(){
	InitInput();
	Ask();
	return 0;
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值