CF1806E 题解

本文最后更新于:2023年4月26日 晚上

题目大意

给你一棵树,然后定义一个函数 $ f(x,y) $,接下来给你 $ q $ 组询问 \(x_{i},y_{i}\),让你求每一次的 $ f(x_{i},y_{i})$。

分析

首先我们尝试根据这个函数的定义暴力求值,代码实现如下。

1
2
3
4
ll BFquery(int g,int h) {
if(!g) return 0;
return 1ll*a[g]*a[h]+BFquery(p[g],p[h]);
}

每次调用这个函数即可。时间复杂度是 $ O(nq) $,不足以通过此题。

于是我们来分析题目中用粗体标注的一个条件:每次给出的 \(x_{i}\)\(y_{i}\),它们深度相同。

这就表明一个点的权值只会和与它处于同一深度的任意一个点相乘,这就减少了相乘点对的组数,也增加了它们出现的次数,会导致我们多次计算同一个 \(f(x,y)\) 的值,增大时间复杂度。

对于这个问题,我们可以尝试用类似记忆化搜索的方法来解决,但是为了不超过空间限制,也不能全部记录,即对于每一组 \(x,y\),我们可以设一个值 \(B\),即在不记录答案的情况下,最多计算 \(B\) 次。对于余下的计算,每次的值都会被存储在一个 map 中,这样时间复杂度可以优化至近似 \(O(Bq)\)

而另外一种方法,存储深度较大的部分而不存储深度较小的部分,目前没有找到这方面正确的做法,如果以后找到我会补上。

下面是代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
int n,q,a[100005]={},p[100005]={},d[100005]={},rp[100005]={},f,s,cnt=0,P;
ll res;
vector<int> v[100005],op[100005];
map<ll,ll> mp;
const int M=100005,B=500;

ll BFquery(int g,int h) {
if(!g) return 0;
if(g>h) swap(g,h);
if(mp[1ll*g*M+h]!=0) return mp[1ll*g*M+h];
else {
mp[1ll*g*M+h]=1ll*a[g]*a[h]+BFquery(p[g],p[h]);
return mp[1ll*g*M+h];
}
}

void wk() {
read(n),read(q);
for(int i=1;i<=n;++i) read(a[i]);
for(int i=2,x;i<=n;++i) {
read(x);
v[x].emplace_back(i);
p[i]=x;
}
while(q--) {
read(f),read(s);
if(f>s) swap(f,s);
res=0;
P=B;
while(f && s && P>0) {
res+=1ll*a[f]*a[s];
f=p[f];
s=p[s];
--P;
}
if(f) res+=BFquery(f,s);
writeln(res);
}
}

CF1806E 题解
https://blog.shuger.ml/CF1806E-sol/
作者
_Shu
发布于
2023年3月21日
许可协议