bzoj4034 T2 树链剖分&树状数组

       一种明显的做法是直接树链剖分然后用区间修改区间查询树状数组(我写的这种)或者线段树来维护吧。。这样做是O(Nlog^2N)的。

       但是还可以做到O(NlogN)。首先可以发现它是单点链上查询,那么可以考虑用差分的思想,或者考虑将单点修改直接变成区间修改然后就只用单点简单查询了。

       首先考虑单点修改,这种操作只对它所在的子树中的所有点的答案有影响,也就是所在的子树中每一个点的答案+a,这个可以用dfs序列转化为区间修改。

       然后考虑对子树x的修改,那么对所在的子树中某一个点p,点p的答案+a*(d[p]-d[x]+1)=+a*d[p]+(a-a*d[x]),注意到括号中的部分是常数(即对每一个p来说相同),用上面的单点修改即可。然后我们还需要一个数组b(另外一个假设是a)维护每一个点的d[p]被加上了多少次。这样还是区间修改。

       那么查询的答案就是ai+bi*d[i]了,直接上查分的树状数组转化为单点修改前缀查询。O(NlogN),这个应该是最快的写法了。

AC代码如下(树链剖分+区间修改区间查询树状数组):

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define N 100005
using namespace std;

int n,m,tot,dfsclk,a[N],fst[N],pnt[N<<1],nxt[N<<1],bg[N],ed[N],fa[N],anc[N],son[N],sz[N];
ll c[2][N];
int read(){
	int x=0,fu=1; char ch=getchar();
	while (ch<'0' || ch>'9'){ if (ch=='-') fu=-1; ch=getchar(); }
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x*fu;
}
void add(int x,int y){
	pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=tot;
}
void ins(int k,int x,ll t){
	for (; x<=n; x+=x&-x) c[k][x]+=t;
}
ll getsum(int k,int x){
	ll sum=0; for (; x; x-=x&-x) sum+=c[k][x]; return sum;
}
void mdy(int x,int y,ll z){
	ins(0,x,z); ins(0,y+1,-z); ins(1,x,z*(x-1)); ins(1,y+1,-z*y);
}
ll qry(int x,int y){
	return getsum(0,y)*y-getsum(1,y)-getsum(0,x-1)*(x-1)+getsum(1,x-1);
}
void dfs(int x){
	sz[x]=1; int p;
	for (p=fst[x]; p; p=nxt[p]){
		int y=pnt[p];
		if (y!=fa[x]){
			fa[y]=x; dfs(y); sz[x]+=sz[y];
			if (sz[y]>sz[son[x]]) son[x]=y;
		}
	}
}
void divide(int x,int tp){
	bg[x]=++dfsclk; anc[x]=tp; int p;
	if (son[x]) divide(son[x],tp);
	for (p=fst[x]; p; p=nxt[p]){
		int y=pnt[p];
		if (y!=son[x] && y!=fa[x]) divide(y,y);
	}
	ed[x]=dfsclk;
}
int main(){
	n=read(); m=read(); int i,k,x,y; ll ans;
	for (i=1; i<=n; i++) a[i]=read();
	for (i=1; i<n; i++){
		x=read(); y=read(); add(x,y); add(y,x);
	}
	dfs(1); divide(1,1);
	for (i=1; i<=n; i++) mdy(bg[i],bg[i],a[i]);
	while (m--){
		k=read();
		if (k==1){
			x=read(); y=read(); mdy(bg[x],bg[x],(ll)y);
		} else if (k==2){
			x=read(); y=read(); mdy(bg[x],ed[x],(ll)y);
		} else{
			x=read(); ans=0;
			for (; x; x=fa[anc[x]]) ans+=qry(bg[anc[x]],bg[x]);
			printf("%lld\n",ans);
		}
	}
	return 0;
}


by lych

2016.3.14

你可能感兴趣的:(线段树,树状数组,树链剖分)