CF1806E Tree Master 题解
直接记忆化搜索即可。
复杂度分析:
下面的被调用是指搜索函数被调用。
-
对于节点数 \(\le\sqrt n\) 的层,我们假设每一层有 \(x\) 个节点,那么最坏情况下会被调用 \(x\choose 2\) 次,是 \(x^2\) 级别的。因为最多有 \(\frac{n}{x}\) 个这样的层,所以总共被调用的次数是 \(\frac{n}{x}\times x^2=nx\) ,因为 \(x\le\sqrt n\),一次调用的时间复杂度是 \(O(1)\),所以总时间复杂度是 \(O(n\sqrt n)\)
-
对于节点数 \(>\sqrt n\) 的层,这样的层小于 \(\sqrt n\) 个,每一层最多被调用 \(q\) 次,所以总共被调用的次数是 \(q\sqrt n\),时间复杂度 \(O(q\sqrt n)\)
综上,总时间复杂度为 \(O(n\sqrt n+q\sqrt n)\),可以通过此题。
一些注意事项:
对于节点数 \(>\sqrt n\) 的层不用记搜反而会更优,因为 unordered_map
的常数巨大,上面分析这一部分的时间复杂度时也跟记搜没有关系,时间复杂度仍然正确。
对于节点数 \(\le\sqrt n\) 的层,直接用数组存储而不用 unordered_map
,unordered_map
常数巨大。
代码(有优化):
#include<bits/stdc++.h>
#define ll long long
#define mxn 100003
#define md 1000000007
#define pb push_back
#define mkp make_pair
#define ld long double
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rept(i,a,b) for(int i=a;i<b;++i)
#define drep(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
int n,q,a[mxn],d[mxn],c[mxn],p[mxn],fa[mxn];
ll f[mxn][500];
ll s[mxn];
ll dfs(int x,int y){
if(x==y)return s[x];
if(c[d[x]]<500){
if(f[x][p[y]])return f[x][p[y]];
return f[x][p[y]]=dfs(fa[x],fa[y])+(ll)a[x]*a[y];
}else return dfs(fa[x],fa[y])+(ll)a[x]*a[y];
}
signed main(){
scanf("%d%d",&n,&q);
rep(i,1,n)scanf("%d",&a[i]);
rep(i,2,n)scanf("%d",&fa[i]);
rep(i,1,n){
s[i]=s[fa[i]]+(ll)a[i]*a[i];
d[i]=d[fa[i]]+1;
p[i]=++c[d[i]];
}
int x,y;
while(q--){
scanf("%d%d",&x,&y);
printf("%lld\n",dfs(x,y));
}
return 0;
}