题解 P10107【[GDKOI2023 提高组] 树】
相当巧妙的倍增/差分题。
题意
给你一颗 个结点的树,点有点权, 次询问给出 ,求:
其中 为 子树中距离 不超过 的所有点, 为 到 的距离。
.
思路
考虑这样一颗树,要求 子树中距离它不超过 的点的答案,即黄色部分。我们可以用绿色部分的答案减去橙色部分的答案得到黄色部分的答案(由于深度相同可以直接减)。
定义一个结点 对应的蓝色区域为所有深度 的点中 小于等于 的结点。如上图中给出了结点 对应的蓝色区域。
定义一个结点 对应的深度为 的绿色区域为所有深度在 中的点中 小于等于 的结点。如上图中给出了结点 对应的深度为 的绿色区域。
那么不妨对于每个深度的结点按 序排序,记 为 在深度为 的点中的前驱, 为 子树中 序最大的子结点(没有子结点则设为 )。
考虑到所求的值中有深度相关的异或运算,需要利用有二进制相关性质的维护方式,不妨考虑使用倍增。记 表示对 进行 次 得到的结点, 为 对应的深度为 的绿色区域的答案。
考虑从 , 的深度为 的绿色区域中所有点的点权都要异或上 ,于是还需要维护 表示 对应的蓝色区域的结点数, 表示 对应的蓝色区域中有几个点的点权第 位为 。用 减去 就可以得到有几个数的第 位会从 变成 ,同理可求有几个数此位从 变成 。
询问时用 的深度为 的绿色区域的答案减去 的即可,考虑求 的答案,先求出 为 进行 次 得到的结点,然后每次从 跳 步到 时, 的蓝色区域减去 的蓝色区域内的点权的第 位都会改变,同上求出即可。
时间复杂度 。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff
}
const int N=1e6+10,L=20;
int n,q,dep[N],v[N],pr[N],rs[L][N],cnt[N],vnt[L][N];
ll f[L][N];
vector<int>a[N],vec[N];
inline void dfs1(int x){
vec[dep[x]].push_back(x);
for(auto t:a[x])
rs[0][x]=t,dep[t]=dep[x]+1,dfs1(t);
}
inline void dfs2(int x){
for(auto t:a[x])
dfs2(t);
cnt[x]+=cnt[rs[0][x]];
for(int k=0;k<L;k++)
vnt[k][x]+=vnt[k][rs[0][x]];
}
inline ll solve(int x,int k){
if(!x) return 0;
k++;
int p=x;
ll ans=0;
for(int i=L-1;i>=0;i--)
if(k>>i&1)
ans+=f[i][p],p=rs[i][p];
for(int i=L-1;i>=0;i--)
if(k>>i&1){
int t=rs[i][x];
int c0=vnt[i][t]-vnt[i][p];
int c1=cnt[t]-cnt[p]-c0;
ans+=(c1-c0)*(1ll<<i);
x=t;
}
return ans;
}
int main(){
n=read();
for(int i=1;i<=n;i++)
v[i]=read();
for(int i=2;i<=n;i++)
a[read()].push_back(i);
dfs1(1);
for(int i=0;i<=n;i++){
if(!vec[i].size()) break;
for(int j=0;j<vec[i].size();j++){
int x=vec[i][j];
if(j) pr[x]=vec[i][j-1];
if(!rs[0][x]) rs[0][x]=rs[0][pr[x]];
f[0][x]=v[x]+f[0][pr[x]];
cnt[x]=1+cnt[pr[x]];
for(int k=0;k<L;k++)
vnt[k][x]=(v[x]>>k&1)+vnt[k][pr[x]];
}
}
dfs2(1);
for(int i=1;i<L;i++)
for(int x=1;x<=n;x++){
rs[i][x]=rs[i-1][rs[i-1][x]];
int t=rs[i-1][x],p=rs[i][x];
int c0=vnt[i-1][t]-vnt[i-1][p];
int c1=cnt[t]-cnt[p]-c0;
f[i][x]=f[i-1][x]+f[i-1][t]+(c1-c0)*(1ll<<i-1);
}
q=read();
while(q--){
int x=read(),k=read();
write(solve(x,k)-solve(pr[x],k)),putc('\n');
}
flush();
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!