bzoj4381 Odwiedziny 树链剖分

       设一个数m,然后将步伐<=m的全部都预处理一下,这样是预处理O(Nm),询问O(1)的;对于步伐>m的,就直接暴力走,这样最多走N/m步,只要能每步O(1)就可以做到询问O(N/m)。

       那么令m=N^0.5;然后考虑怎么走,首先要快速求出lca,那么如果用树链剖分,走的时候在重链上面走,然后重链和重链之间用logN查询。这样就是O(log^N+N^0.5),查询lca可以用树链剖分醉倒O(logN)。那么总的时间复杂度为:

       O(N^1.5+Mlog^2N+MN^0.5)

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define N 50005
using namespace std;

int n,m,tot,dfsclk,a[N],b[N],fa[N],d[N],sz[N],s[N][155],son[N],anc[N];
int pos[N],id[N],fst[N],pnt[N<<1],nxt[N<<1];
int read(){
	int x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
void add(int x,int y){
	pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=tot;
}
void dfs(int x){
	int i,p=fa[x]; sz[x]=1;
	for (i=1; i<=m && p; i++,p=fa[p]) s[x][i]=s[p][i]+a[x];
	for (p=fst[x]; p; p=nxt[p]){
		int y=pnt[p];
		if (y!=fa[x]){
			fa[y]=x; d[y]=d[x]+1; dfs(y); sz[x]+=sz[y];
			if (sz[y]>sz[son[x]]) son[x]=y;
		}
	}
}
void divide(int x,int tp){
	anc[x]=tp; id[pos[x]=++dfsclk]=x; int p;
	if (son[x]) divide(son[x],tp);
	for (p=fst[x]; p; p=nxt[p]){
		int y=pnt[p];
		if (y!=fa[x] && y!=son[x]) divide(y,y);
	}
}
int lca(int x,int y){
	for (; anc[x]!=anc[y]; x=fa[anc[x]])
		if (d[anc[x]]<d[anc[y]]) swap(x,y);
	return (d[x]<d[y])?x:y;
}
int find(int x,int dep){
	for (; d[x]-d[anc[x]]<dep; x=fa[anc[x]]) dep-=d[x]-d[anc[x]]+1;
	return id[pos[x]-dep];
}
int qry(int x,int y,int z){
	if (z<=m) return s[x][z]-s[y][z]+a[y]; int i,sum=0;
	for (; anc[x]!=anc[y]; x=find(x,pos[x]-i))
		for (i=pos[x]; i>=pos[anc[x]]; i-=z) sum+=a[id[i]];
	for (i=pos[x]; i>=pos[y]; i-=z) sum+=a[id[i]];
	return sum;
}
int solve(int x,int y,int z){
	int ans=0,tmp=lca(x,y),len=d[x]+d[y]-(d[tmp]<<1);
	if (len%z) ans+=a[y]; y=find(y,len%z);
	int t1=find(x,(d[x]-d[tmp])/z*z);
	if (d[y]<=d[tmp]) return ans+qry(x,t1,z);
	int t2=find(y,(d[y]-d[tmp])/z*z);
	if (t1==t2) ans-=a[t1]; return ans+qry(x,t1,z)+qry(y,t2,z);
}
int main(){
	n=read(); m=((int)sqrt(n))/7*3+1; int i,x,y;
	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++) b[i]=read();
	for (i=1; i<n; i++)
		printf("%d\n",solve(b[i],b[i+1],read()));
	return 0;
}


by lych

2016.3.29

你可能感兴趣的:(LCA,树链剖分)