WC2013-糖果公园

给出一棵树,每个点有颜色,每种颜色有一个\(v\)属性,第\(i\)次经过颜色\(j\)可以获得\(w_i*v_j\)的价值。多次修改单点颜色,查询路径的价值。\(n,m,q\le 10^5\)

分析

可以发现,经过颜色的顺序是没有关系的,只需要知道有多少个颜色就可以了。也就是说,这个问题可以快速转移,修改是单点修改,加上可以离线,使用莫队算法。

算是重新学了一次莫队。现有的莫队算法有两种,线上普通莫队和线上带修改莫队。树上莫队只不过是把线上的情况用欧拉序的方法变成了线上的(用于链上询问)或者用dfs序变成线上的(用于子树询问)。下面讨论普通莫队和带修改莫队。

一般莫队算法

对于离线区间无修改询问,莫队算法是一种很优秀的处理方式。莫队算法的思想是通过询问排序和快速转移解决询问。一般地,如果对于区间\([l,r]\)的询问可以转移到\([l-1,r],[l,r-1],[l+1,r],[l,r+1]\),那么这个问题就可以用莫队处理。

我们把序列分块,设块大小为\(B\),块数为\(S\),显然有\(B*S=n\)。把所有询问按照左端点所在块为第一关键字,右端点位置为第二关键字排序,按这样的顺序处理每个询问。初始时\(l,r\)都在0的位置。

对于处理到的当前询问,我们直接暴力把l和r移动到相应的位置。设序列长度为\(n\),询问数为\(m\),分析复杂度:

  • 对于左端点
    • 左端点在块内的移动每次最多为\(B\),最多移动\(m\)次,所以复杂度为\(O(mB)\)
    • 左端点在块之间移动最多发生\(S\)次,每次最多跳\(B\)个位置,所以复杂度为\(O(BS)=O(n)\)
  • 对于右端点
    • 左端点在一个块中的时候,右端点是从小到大的,所以最多移动\(m\)次,左端点有\(S\)个可以在的块,所以复杂度为\(O(Sm)\)
    • 左端点在块之间移动的时候,右端点最多移动\(n\)个位置,一共会发生\(nS\)次,复杂度为\(O(nS)\)

这样分析,设一次转移的复杂度为\(O(k)\),那么总复杂度为\(O(k(mB+n+Sm+nS))\),当\(B=\sqrt \frac{n(m+n)}{m}\)的时候取到最小值。当\(n,m\)同阶的时候,复杂度取到\(O(n^\frac{3}{2}k)\)

带修改莫队算法

莫队带上了修改,那么每次询问其实变成了三维,\((l,r,t)\),分别为询问的左右端点和这是在第几次修改后的询问。

做法如下,左右端点都按照所在块编号排序,时间从小到大排序,这样处理询问。分析三维的移动:

  • 左右端点的移动同普通莫队左端点的移动分析,复杂度都为\(O(mB+n)\)
  • 时间的移动,由于是按照左右端点所在块排序后从小到大排序,所以当左右端点的块是确定的时候,时间的移动是最多\(O(m)\)的,而左右端点所在块最多为\(O(S^2)\)种,所以复杂度为\(O(mS^2)\)

\(B\)取到\(n^\frac{2}{3}\)时,复杂度达到\(O(n^\frac{5}{3}k)\)

这样来看,这道题就是一个很简单的带修改莫队啦~

代码

跑的比本地快啊,只用了16s。有一个需要注意的地方,当\(n^\frac{2}{3}<1\)的时候,要取个max,这样就不会出现除以0的问题了。

#include<cstdio>
#include<cstring>
#include<cctype>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long giant;
const int theb=1e7;
char buf[theb],*pt=buf;
int read() {
	int x=0,f=1;
	for (;!isdigit(*pt);++pt) if (*pt=='-') f=-1;
	for (;isdigit(*pt);++pt) x=x*10+*pt-'0';
	return x*f;
}
void write(giant x) {
	if (!x) puts("0"); else {
		static char s[25];
		int tot=0;
		while (x) s[++tot]=x%10+'0',x/=10;
		while (tot) putchar(s[tot--]);
		puts("");
	}
}
const int maxn=2e5+10;
const int maxj=17;
giant theans=0,ans[maxn];
int blos,n,m,q,col[maxn],cs[maxn],block[maxn],dep[maxn];
giant val[maxn],w[maxn];
int aux[maxn],first[maxn],second[maxn],dfn[maxn],dft=0,current;
bool ina[maxn];
struct C {
	int p,ord,nw;
} change[maxn];
struct Q {
	int u,v,l,r,t,id;
	inline bool operator < (const Q &a) const {
		if (block[l]!=block[a.l]) return block[l]<block[a.l];
		if (block[r]!=block[a.l]) return block[r]<block[a.r];
		return t<a.t;
	}
} qs[maxn];
struct graph {
	struct edge {
		int v,nxt;
	} e[maxn<<1];
	int h[maxn],tot,f[maxn][maxj];
	graph ():tot(0) {}
	void add(int u,int v) {
		e[++tot]=(edge){v,h[u]};
		h[u]=tot;
	}
	void dfs(int x,int fa) {
		f[x][0]=fa;
		dep[x]=dep[fa]+1;
		for (register int j=1;j<maxj;++j) f[x][j]=f[f[x][j-1]][j-1];
		first[x]=++dft;
		dfn[dft]=x;
		for (register int i=h[x],v=e[i].v;i;i=e[i].nxt,v=e[i].v) if (v!=fa) dfs(v,x);
		second[x]=++dft;
		dfn[dft]=x;
	}
	int lca(int x,int y) {
		if (dep[x]<dep[y]) swap(x,y);
		for (register int j=maxj-1;j>=0;--j) if (dep[f[x][j]]>=dep[y]) x=f[x][j];
		if (x==y) return x;
		for (register int j=maxj-1;j>=0;--j) if (f[x][j]!=f[y][j]) x=f[x][j],y=f[y][j];
		return f[x][0];
	}
} G;
inline void flow(int x) {
	C &c=change[x];
	if (ina[c.p]) theans-=w[cs[col[c.p]]--]*val[col[c.p]],theans+=w[++cs[col[c.p]=c.nw]]*val[col[c.p]]; 
	else col[c.p]=c.nw;
}
inline void back(int x) {
	C &c=change[x];
	if (ina[c.p]) theans-=w[cs[col[c.p]]--]*val[col[c.p]],theans+=w[++cs[col[c.p]=c.ord]]*val[col[c.p]];
	else col[c.p]=c.ord;
}
inline void in(int x) {
	theans+=w[++cs[col[x]]]*val[col[x]];
}
inline void out(int x) {
	theans-=w[cs[col[x]]--]*val[col[x]];
} 
inline void deal(int x) {
	x=dfn[x];
	ina[x]?out(x):in(x);
	ina[x]^=true;
}
inline giant add(int x) {
	return w[cs[col[x]]+1]*val[col[x]];
}
int main() {
#ifndef ONLINE_JUDGE
	freopen("test.in","r",stdin);
	freopen("my.out","w",stdout);
#endif
	fread(buf,1,theb,stdin);
	n=read(),m=read(),q=read();
	blos=max((int)pow(n*2,2.0/3.0)/3,1);
	for (register int i=1;i<=(n<<1);++i) block[i]=(i-1)/blos+1;
	for (register int i=1;i<=m;++i) val[i]=read();
	for (register int i=1;i<=n;++i) w[i]=read();
	for (register int i=1;i<n;++i) {
		int u=read(),v=read();
		G.add(u,v),G.add(v,u);
	}
	for (register int i=1;i<=n;++i) col[i]=aux[i]=read();
	G.dfs(1,1);
	int tm=0,all=0;
	for (register int i=1;i<=q;++i) {
		int op=read(),u=read(),v=read();
		if (op) {
			if (dep[u]>dep[v]) swap(u,v);
			int lca=G.lca(u,v);
			if (lca!=u && first[u]>first[v]) swap(u,v); 
			int l,r;
			if (u==lca) l=first[u],r=first[v]; else l=second[u],r=first[v];
			++all;
			qs[all]=(Q){u,v,l,r,tm,all};
		} else {
			change[++tm]=(C){u,aux[u],v};
			aux[u]=v;
		}
	}
	sort(qs+1,qs+all+1);
	int nl=1,nr=0,nt=0;
	for (register int i=1;i<=all;++i) {
		current=i;
		int u=qs[i].u,v=qs[i].v,t=qs[i].t;
		int lca=G.lca(u,v);
		int l=qs[i].l,r=qs[i].r;
		while (nt<t) flow(++nt);
		while (nt>t) back(nt--);
		while (nl>l) deal(--nl);
		while (nr<r) deal(++nr);
		while (nl<l) deal(nl++);
		while (nr>r) deal(nr--);
		ans[qs[i].id]=theans+(!ina[lca]?add(lca):0);
	}
	for (register int i=1;i<=all;++i) write(ans[i]);
	return 0;
}
posted @ 2017-05-07 11:57  permui  阅读(364)  评论(0编辑  收藏  举报