设一个数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