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_mapunordered_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;
}
posted @ 2024-02-27 22:05  zifanwang  阅读(5)  评论(0编辑  收藏  举报