题意
每次询问给两个同深度的点,答案为前缀路径两两的权值相乘。
constrain:
1
≤
n
,
q
≤
1
e
5
1\leq n,q\leq 1e5
1≤n,q≤1e5
思路
复杂度证明题,最坏情况q=n时,可以证明所有询问下来,暴力枚举遇到的点对(x,y)的数目不会超过
n
n
n\sqrt{n}
nn个.假设
i
i
i层有
s
i
s_i
si个节点,那么他对点对的贡献为
m
i
n
(
s
i
2
,
n
)
min(s_i^2,n)
min(si2,n),可以发现给每层分配
n
\sqrt{n}
n个点的时候,情况最多,
O
(
∑
s
i
2
)
=
O
(
n
n
)
O(\sum s_i^2)=O(n\sqrt n)
O(∑si2)=O(nn),这是用了基本不等式放大。
实现时,不需要用一个二维map来存
<
x
,
y
>
=
a
x
a
y
<x,y> = a_xa_y
<x,y>=axay,对于没见过的
(
x
,
y
)
(x,y)
(x,y)暴力递归算答案,见过的直接拿答案。当某一层深度大于
n
\sqrt n
n时,我们不用记录那一层的信息。因为这种层的数目小于
n
\sqrt n
n,整个时间复杂度不会变
O
(
q
n
+
n
n
)
O(q\sqrt n+n\sqrt n)
O(qn+nn)前面时不记录的层的时间,后面是记录的不同点对的时间。
代码
#include<bits/stdc++.h>
#define ll long long
#define fir first
#define sec second
#define pii pair<int,int>
using namespace std;
const int maxn=100005;
const int sqrtn=325;
const int B=320;
const int inf=0x3f3f3f3f;
int n,q;
int a[maxn];
int h[maxn];
int fa[maxn];
int cnt[maxn];
int depth[maxn];
ll f[maxn][sqrtn];
vector<int> tree[maxn];
void dfs(int x,int d) {
h[x]=++cnt[d],depth[x]=d;
for(int to:tree[x]) {
dfs(to,d+1);
}
}
ll ask(int x,int y) {
if(!x&&!y) {
return 0;
}
if(cnt[depth[y]]<=B&&f[x][h[y]]) { //h[y]表示y在那一层的下标
return f[x][h[y]];
}
ll ans=ask(fa[x],fa[y])+1ll*a[x]*a[y];
if(cnt[depth[y]]<=B) {
f[x][h[y]]=ans;
}
return ans;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>q;
for(int i=1;i<=n;i++) {
cin>>a[i];
}
for(int i=2;i<=n;i++) {
cin>>fa[i];
tree[fa[i]].push_back(i);
}
dfs(1,0);
while(q--) {
int x,y;
cin>>x>>y;
cout<<ask(x,y)<<"\n";
}
}