P4074 [WC2013]糖果公园 题解

这道题可以说是一道树上带修莫队的板子题。虽然评级是黑的,但是树上带修莫队本身还是比较好想的。就是代码很难调。

update 2021/4/12:现在评级掉紫了。

树上莫队:

树上莫队的本质就是利用欧拉序将树上莫队问题变成序列莫队问题。

我们设 \(\{eular\}\) 表示欧拉序序列, \(fir_i,las_i\) 表示节点 \(i\) 在欧拉序中第一次出现与第二次出现的位置。那么当询问为 \(x,y\) 时(此处我们规定 \(x\) 的深度小于 \(y\)),如果 \(lca(x,y)=x\) ,那么用 \(fir_x,fir_y\) 的区间,否则用 \(las_x,fir_y\) 的区间同时带上 \(lca(x,y)\) 的贡献。为什么不是 \(fir_x\) ?因为中间出现二次的节点我们是不考虑的,因此用 \(fir_x\) 相当于浪费时间 (用时间换空间的当我没说) 。这里千万注意:序列长度是 \(2n\) 而不是 \(n\) ,千万不要在这里 TLE 了!

带修莫队:

带修莫队的本质就是加一个时间轴,让指针除了在 \(l,r\) 上动还可以在 \(time\) 上动,将询问与修改分开储存,就可以完成了。

树上带修莫队:

实际上就是前面两个的结合体,先跑一遍欧拉序,然后再跑一遍带修莫队即可。

三个注意点:

  1. 在跑莫队的时候,如果要计算 \(lca(x,y)\) 的贡献(这里我们规定 \(lca(x,y)\) 不在询问的区间内,如果在可以前面直接先特判一下),算完之后一定不要忘记还原
  2. 再次提醒:数列长度是 \(2n\) ,千万不能在这里 TLE 了!
  3. 这里块长取 \(eular^{\frac{2}{3}}\) ,比 \(\sqrt{eular}\) 要好一点,其中 \(eular\) 表示欧拉序的长度

几个小优化:

  1. 如果可以,使用树链剖分求 \(lca\)
  2. 吸氧。(经过实测,我一开始调的块长是 \(n^{\frac{2}{3}}\),调错了,但是吸氧之后 \(30pts\) \(->\) \(70pts\)

代码:

#include<bits/stdc++.h>
using namespace std;

const int MAXN=1e6+5;
int n,m,que,v[MAXN],w[MAXN],c[MAXN],fir[MAXN],las[MAXN],ys[MAXN],block,fa[MAXN][21],eular[MAXN<<1],dep[MAXN],cntq,cntc,vis[MAXN];
typedef long long LL;
LL ans[MAXN],total,cnt[MAXN];
vector<int>Next[MAXN];
struct query
{
	int l,r,id,lca,Time;
}q[MAXN];
struct change
{
	int pos,val;
}cha[MAXN];

int read()
{
	int sum=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') {sum=(sum<<3)+(sum<<1)+(ch^48);ch=getchar();}
	return sum;
}

void dfs(int x)
{
	eular[++eular[0]]=x;
	fir[x]=eular[0];
	for(int i=0;i<Next[x].size();i++)
	{
		int u=Next[x][i];
		if(dep[u]) continue;
		dep[u]=dep[x]+1;
		fa[u][0]=x;
		dfs(u);
	}
	eular[++eular[0]]=x;
	las[x]=eular[0];
}

void st()
{
	for(int i=1;i<=20;i++)
		for(int j=1;j<=n;j++)
			fa[j][i]=fa[fa[j][i-1]][i-1];
}

int getlca(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	int d=dep[x]-dep[y],tmp=-1;
	while(d)
	{
		tmp++;int p=d&1;d>>=1;
		if(p) x=fa[x][tmp];
	}
	if(x==y) return x;
	for(int i=20;i>=0;i--)
		if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}

bool cmp(const query &fir,const query &sec)
{
	if(ys[fir.l]^ys[sec.l]) return ys[fir.l]<ys[sec.l];
	if(ys[fir.r]^ys[sec.r]) return ys[fir.r]<ys[sec.r];
	return fir.Time<sec.Time;
}

void add(int x)
{
	total+=1ll*v[c[x]]*w[++cnt[c[x]]];
}
void del(int x)
{
	total-=1ll*v[c[x]]*w[cnt[c[x]]--];
}

void work(int x)
{
	vis[x]?del(x):add(x);
	vis[x]^=1;
}

void deal(int t)
{
	if(vis[cha[t].pos])
	{
		work(cha[t].pos);
		swap(c[cha[t].pos],cha[t].val);
		work(cha[t].pos);
	}
	else swap(c[cha[t].pos],cha[t].val);
}

int main()
{
	n=read();m=read();que=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();
		Next[x].push_back(y);
		Next[y].push_back(x);
	}
	for(int i=1;i<=n;i++) c[i]=read();
	fa[1][0]=1;dep[1]=1;dfs(1);st();block=ceil(pow(eular[0],2.0/3.0));
	for(int i=1;i<=(n<<1);i++) ys[i]=(i-1)/block+1;
	for(int i=1;i<=que;i++)
	{
		int opt=read(),zzh1=read(),zzh2=read();
		if(opt)
		{
			q[++cntq].id=cntq;
			q[cntq].Time=cntc;
			if(fir[zzh1]>fir[zzh2]) swap(zzh1,zzh2);
			int qlca=getlca(zzh1,zzh2);
			if(zzh1==qlca) q[cntq].l=fir[zzh1],q[cntq].r=fir[zzh2];
			else q[cntq].l=las[zzh1],q[cntq].r=fir[zzh2],q[cntq].lca=qlca;
		}
		else
		{
			cha[++cntc].pos=zzh1;
			cha[cntc].val=zzh2;
		}
	}
	sort(q+1,q+cntq+1,cmp);
	int l=1,r=0,t=0;
	for(int i=1;i<=cntq;i++)
	{
		while(l<q[i].l) work(eular[l++]);
		while(l>q[i].l) work(eular[--l]);
		while(r<q[i].r) work(eular[++r]);
		while(r>q[i].r) work(eular[r--]);
		while(t<q[i].Time) deal(++t);
		while(t>q[i].Time) deal(t--);
		if(q[i].lca) work(q[i].lca);
		ans[q[i].id]=total;
		if(q[i].lca) work(q[i].lca);
	}
	for(int i=1;i<=cntq;i++) printf("%lld\n",ans[i]);
	return 0;
}
posted @ 2022-04-12 21:43  Plozia  阅读(55)  评论(0编辑  收藏  举报