YBTOJ&洛谷P4074:糖果公园(树上莫队)

46 篇文章 0 订阅
18 篇文章 0 订阅

文章目录

所谓树上莫队,就是在树上的莫队

(逃)
传送门

解析

似乎就是树上的这道题
考虑如何转化为序列问题呢?
考虑dfs序
但是又一个问题。。。
似乎这条链的dfs序不连续啊
树剖一下就好啦
考虑更阳间的方法
求出这棵树的欧拉序,在这个欧拉序上询问
那么我们发现,这样的话,其实会多算的部分就都会多算2遍
比如样例:
在这里插入图片描述

以1为根,欧拉序为:
13442231
那么我们考虑(4-3)
对应的序列就是:

44223

不在路径上的2恰好算了2次
所以我们可以利用异或的性质
还有一些特判的问题:

  1. lca不在端点时,需要额外计算lca
  2. 左端点不时lca时,需要额外计算左端点
    画画图就很清楚了

update:

上面那个特判是洛谷野生题解的,有点过于阴间了…
ybt的实现优美的多(至少易于记忆)
设一个点进入和离开dfs的时间分别为 i n ( x ) , o u t ( x ) in(x),out(x) in(x),out(x)
i n ( x ) < i n ( y ) in(x)<in(y) in(x)<in(y),分情况讨论:

  1. x x x y y y 的祖先,那么答案就是 [ i n ( x ) , i n ( y ) ] [in(x),in(y)] [in(x),in(y)]
  2. x x x 不是 y y y 的祖先,那么在 [ o u t ( x ) , i n ( y ) ] [out(x),in(y)] [out(x),in(y)] 的基础上,还要加上 l c a ( x , y ) lca(x,y) lca(x,y)

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=3e5+100;
const int M=1050;
const int mod=998244353;
const double eps=1e-6;
ll read(){
	ll x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
	return x*f;
}
int n,m;
struct node{
	int to,nxt;
}p[N<<1];
int fi[N],cnt;
void addline(int x,int y){
	p[++cnt]=(node){y,fi[x]};fi[x]=cnt;
}
int pl[N][22],dep[N],siz[N];
ll v[N],w[N];
int pos[N],dfn[N],ed[N],Tim;
void dfs(int x,int f){
	pl[x][0]=f;pos[x]=++Tim;dfn[Tim]=x;siz[x]=1;
	for(int k=1;k<=18;k++) pl[x][k]=pl[pl[x][k-1]][k-1];
	for(int i=fi[x];~i;i=p[i].nxt){
		int to=p[i].to;
		if(to==f) continue;
		dep[to]=dep[x]+1;
		dfs(to,x);
		siz[x]+=siz[to];
	}
	dfn[++Tim]=x;ed[x]=Tim;
}
bool vis[N];
int a[N],tim,tot,from[N],to[N],id[N],bel[N];
int Lca(int x,int y){
	if(dep[x]<dep[y]) swap(x,y);
	for(int k=18;k>=0;k--){
		if(!pl[x][k]||dep[pl[x][k]]<dep[y]) continue;
		x=pl[x][k];
	}
//	printf("LCA:x=%d y=%d ",x,y);
	if(x==y){
//		printf("ares=%d\n",x);
		return x;
	}
//	printf("  mid:x=%d y=%d ",x,y);
	for(int k=18;k>=0;k--){
		if(pl[x][k]==pl[y][k]) continue;
		x=pl[x][k];y=pl[y][k];
	}
//	printf("x=%d res=%d\n",x,pl[x][0]);
	return pl[x][0];
}
struct query{
	int l,r,t,id,lca;
	bool operator < (const query o)const{
		if(bel[l]!=bel[o.l]) return bel[l]<bel[o.l];
		else if(bel[r]!=bel[o.r]) return bel[r]<bel[o.r];
		else return t<o.t;
	}
}ask[N];
int l,r,t,bac[N];
ll now;
ll ans[N];
inline void work(int x){
	x=dfn[x];
	(vis[x]^=1)?now+=v[a[x]]*w[++bac[a[x]]]:now-=v[a[x]]*w[bac[a[x]]--];
}
int q;
int ww;
int main(){
	memset(fi,-1,sizeof(fi));cnt=-1;
	n=read();m=read();q=read();
	for(int i=1;i<=m;i++) v[i]=read();
	for(int i=1;i<=n;i++) w[i]=read();
	for(int i=1;i<n;i++){
		int x=read(),y=read();
		addline(x,y);addline(y,x);
	}
	for(int i=1;i<=n;i++) a[i]=read();
	dep[1]=1;dfs(1,0);
//	for(int i=1;i<=Tim;i++) printf("%d ",dfn[i]);
//	printf("\n");
	ww=floor(pow(Tim,2.0/3));
	for(int i=1;i<=Tim;i++){
		bel[i]=(i-1)/ww+1;
	}
	for(int i=1;i<=q;i++){
		int op=read(),x=read(),y=read();
		if(op==0){
			++tim;int o=x;
			from[tim]=a[o];id[tim]=o;
			a[o]=y;to[tim]=a[o];
		}
		else{
			if(pos[x]>pos[y]) swap(x,y);
			ask[++tot]=(query){pos[x],pos[y],tim,tot,pos[Lca(x,y)]};
		}
//		printf("i=%d op=%d x=%d y=%d\n",i,op,x,y);
	}
	sort(ask+1,ask+1+tot);
	l=1;t=tim;
	for(int i=1;i<=tot;i++){
		int nl=ask[i].l,nr=ask[i].r,nt=ask[i].t,lca=ask[i].lca;
		while(l<nl) work(l++);
		while(l>nl) work(--l);
		while(r<nr) work(++r);
		while(r>nr) work(r--);
		while(t<nt){
			t++;
			int o=id[t],f=0;
			if((pos[o]<l&&l<=ed[o]&&ed[o]<=r)) f=1;
			else if(l<=pos[o]&&pos[o]<=r&&r<ed[o]) f=2;
			if(f==1) work(ed[o]);
			else if(f==2) work(pos[o]);
			a[o]=to[t];
			if(f==1) work(ed[o]);
			else if(f==2) work(pos[o]);
		}
		while(t>nt){
			int o=id[t],f=0;
			if((pos[o]<l&&l<=ed[o]&&ed[o]<=r)) f=1;
			else if(l<=pos[o]&&pos[o]<=r&&r<ed[o]) f=2;
			if(f==1) work(ed[o]);
			else if(f==2) work(pos[o]);
			a[o]=from[t];
			if(f==1) work(ed[o]);
			else if(f==2) work(pos[o]);
			t--;
		}
		if(nl!=lca){
			work(nl);
			if(nr!=lca) work(lca);
		}
//		printf("id=%d (%d %d %d %d) res=%lld\n",ask[i].id,nl,nr,nt,lca,now);
		ans[ask[i].id]=now;
		if(nl!=lca){
			work(nl);
			if(nr!=lca) work(lca);
		}
	}
	for(int i=1;i<=tot;i++) printf("%lld\n",ans[i]);
	return 0;
}
/*

*/
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值