题解 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;
}
posted @ 2023-03-19 00:21  xiaolilsq  阅读(68)  评论(0编辑  收藏  举报