题解 CF1806E Tree Master
好久没写博客了写一篇。
额,这题暴力记忆化就可以了,一开始属实是想不到。。。
暴力记忆化时间复杂度不超过 \(O\left(n\sqrt{n}\right)\) 。依次考虑每一层,如果某一层的点数不超过 \(B\) ,认为这一层都是需要预处理的,如果 \(i\) 需要预处理,那么认为 \(i\) 的父亲也需要预处理,由此就可以得到一个需要预处理的点集 \(S\) ,并且 \(S\) 中同一层的点数不超过 \(B\) ,对于同一层中每一个点对都预处理得到答案,那么需要预处理的点对数不超过 \(nB\) 。每次查询的时候就两个点同时暴力往上跳,直到跳到一个预处理过的点对,容易发现存在至少一个点没有被预处理过的层的点数都是大于 \(B\) 的,所以这样的层数是不超过 \(n/B\) 的,最多跳 \(n/B\) 次就可以得到答案,所以复杂度不超过 \(O(nB+qn/B)\) ,认为 \(n\) 和 \(q\) 同阶, \(B\) 取 \(\sqrt{n}\) 复杂度就是 \(O\left(n\sqrt{n}\right)\) 。
以下是预处理版本的代码:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=100005,maxsq=410;
int a[maxn],f[maxn],dp[maxn];
int CNT[maxn],NUM[maxn];
int sum[maxn],vis[maxn],az[maxn],za;
long long Ans[maxn][maxsq];
long long GetAns(int x,int y){
long long re=0;
while(x&&!Ans[x][vis[y]]){
re+=1ll*a[x]*a[y];
x=f[x];y=f[y];
}
return re+Ans[x][vis[y]];
}
bool cmp(int x,int y){
return dp[x]==dp[y]?vis[x]<vis[y]:dp[x]<dp[y];
}
int main(){
// freopen("test.in","r",stdin);
int n,q;scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i)scanf("%d",&a[i]);
++CNT[dp[1]=1];NUM[1]=1;
for(int i=2;i<=n;++i){
scanf("%d",&f[i]);
dp[i]=dp[f[i]]+1;
++CNT[dp[i]];
}
int tmp=400;
for(int i=n;i>=1;--i){
if(CNT[dp[i]]<=tmp||vis[i]){
vis[f[i]]=vis[i]=true;
}
}
for(int i=1;i<=n;++i){
if(vis[i]){
vis[i]=++sum[dp[i]];
az[++za]=i;
}
}
sort(az+1,az+za+1,cmp);
for(int i=1;i<=za;++i){
int u=az[i];
for(int j=i;j<=za&&dp[az[i]]==dp[az[j]];++j){
int v=az[j];
Ans[u][vis[v]]=Ans[v][vis[u]]=1ll*a[u]*a[v]+Ans[f[u]][vis[f[v]]];
}
}
while(q--){
int x,y;
scanf("%d%d",&x,&y);
printf("%lld\n",GetAns(x,y));
}
return 0;
}