1. 程式人生 > 其它 >P4074 [WC2013]糖果公園 題解

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;
}