[WC2013] 糖果公园

链接

小·清·新 莫队题

询问和区间内某一种数字的出现次数有关,而且还带修,那大概率就是莫队了。

但是我们发现这次是链查。而莫队是序列算法,不能在树上移指针。

所以我们要尝试化树为序列。所以个人感觉叫“树上莫队”是挂羊头卖狗肉

总结常用的树序列和性质:

  1. dfs序(第一次访问时记录):一个子树内的dfs序为一个连续区间,且一个非叶子节点的某个儿子和它dfs序连续。
  2. 欧拉序(访问一次记录一次):一条链 (u ightarrow v) 上的所有点都在 ([l_u,l_v]) 上出现过,且 (u,v) 的非链上的祖先没有出现过。
  3. 括号序(第一次/最后一次访问时记录):一个点 (u) 到根路径上的所有点在 ([1,l_u]) 中恰好出现一次。

显然,这题用括号序更加妥当。

但是括号序的性质是一个点 (u) 到根路径上的点出现一次。那么链 (u ightarrow v)(假设 (l_u<l_v))应该对应 ((l_u,l_v])

但是手算一下,发现他们的lca也被除掉了。所以再加上就好了。

那么这样我们就把树转换成序列问题。接下来我们只要统计区间内出现两次的数字即可。这个对于莫队来说没有什么难处。

那么接下来就是处理修改了。这个也就是套一个带修莫队就行了。

具体来说,我们需要给每个询问多加一维 (t),即它的统计是在第 (t) 个修改操作后。

然后我们在移动指针的时候,将修改的指针移动也加进去。即如果一个修改操作在当前询问区间内那么处理修改的贡献,然后再修改。

理论复杂度 (O(n^{frac 5 3}))但是莫队的复杂度你也信?

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 800010
#define ll long long
using namespace std;
int nxt[N<<1],to[N<<1],head[N],cnt;
void add(int u,int v)
{
	nxt[++cnt]=head[u];
	to[cnt]=v;
	head[u]=cnt;
}
int ld[N],rd[N];
int qt[N],tot;
int dep[N],fa[N],son[N],siz[N];
int top[N];
void dfs1(int u,int p)
{
	dep[u]=dep[p]+1;
	fa[u]=p;
	siz[u]=1;
	qt[ld[u]=++tot]=u;
	for(int i=head[u];i;i=nxt[i])
	{
		int v=to[i];
		if(v==fa[u]) continue;
		dfs1(v,u);
		if(siz[son[u]]<siz[v]) son[u]=v;
		siz[u]+=siz[v];
	}
	qt[rd[u]=++tot]=u;
}
void dfs2(int u,int topp)
{
	top[u]=topp;
	if(son[u]) dfs2(son[u],topp);
	for(int i=head[u];i;i=nxt[i])
	{
		int v=to[i];
		if(v!=fa[u] && v!=son[u]) dfs2(v,v);
	}
}
int lca(int u,int v)
{
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		u=fa[top[u]];
	}
	return dep[u]<dep[v]?u:v;
}
int cx[N],cy[N],cp[N],bl[N];
struct node{
	int l,r,t,id,ex;
	bool operator <(const node a)const
	{
		if(bl[l]!=bl[a.l]) return bl[l]<bl[a.l];
		if(bl[r]!=bl[a.r]) return bl[r]<bl[a.r];
		return t<a.t;
	}
}q[N];
int qtot;
ll w[N],val[N],ans[N];
int vis[N],c[N];
ll wcnt[N],res;
int fl=1,fr=0,ft=0;
inline void ins(int x){wcnt[x]++; res+=val[x]*w[wcnt[x]];}
inline void ers(int x){res-=val[x]*w[wcnt[x]]; wcnt[x]--;}
inline void add(int x){vis[x]?ers(c[x]):ins(c[x]); vis[x]^=1;}
inline void del(int x){vis[x]^=1; vis[x]?ins(c[x]):ers(c[x]);}
inline void addq(int x)
{
	if(vis[cx[x]]) ers(c[cx[x]]);
	c[cx[x]]=cy[x];
	if(vis[cx[x]]) ins(c[cx[x]]);
}
inline void delq(int x)
{
	if(vis[cx[x]]) ers(c[cx[x]]);
	c[cx[x]]=cp[x];
	if(vis[cx[x]]) ins(c[cx[x]]);
}
int main()
{
	int n,k,m;
	scanf("%d%d%d",&n,&k,&m);
	for(int i=1;i<=k;i++) scanf("%lld",&val[i]);
	for(int i=1;i<=n;i++) scanf("%lld",&w[i]);
	for(int i=1;i<n;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v),add(v,u);
	}
	dfs1(1,0);
	dfs2(1,1);
	for(int i=1;i<=n;i++) scanf("%d",&c[i]);
	int tm=0;
	for(int i=1;i<=m;i++)
	{
		int opt,l,r;
		scanf("%d%d%d",&opt,&l,&r);
		if(opt==1)
		{
			if(ld[l]>ld[r]) swap(l,r);
			int lc=lca(l,r);
			if(lc==l) q[++qtot]=(node){ld[l],ld[r],tm,i,0};
			else q[++qtot]=(node){rd[l],ld[r],tm,i,lc};
		}
		else
		{
			++tm;
			cx[tm]=l,cy[tm]=r;
		}
	}
	for(int i=1;i<=tm;i++) cp[i]=c[cx[i]],c[cx[i]]=cy[i];
	for(int i=tm;i>=1;i--) c[cx[i]]=cp[i];
	const int B=pow(n,2.0/3)*0.5+1;
	for(int i=1;i<=3*n;i++) bl[i]=i/B;
	sort(q+1,q+qtot+1);
	for(int i=1;i<=qtot;i++)
	{
		while(fr<q[i].r) add(qt[++fr]);
		while(fr>q[i].r) del(qt[fr--]);
		while(fl<q[i].l) del(qt[fl++]);
		while(fl>q[i].l) add(qt[--fl]);
		while(ft<q[i].t) addq(++ft);
		while(ft>q[i].t) delq(ft--);
		if(q[i].ex) add(q[i].ex);
		ans[q[i].id]=res;
		if(q[i].ex) del(q[i].ex);
	}
	for(int i=1;i<=m;i++)
	if(ans[i]) printf("%lld
",ans[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/Flying2018/p/13626738.html