题目链接:点击查看
题目大意:给出一棵树,初始时只有一个节点1,权值为0,后续有 n 个操作,每次操作分为两种情况:
- 1 u val:向树中插入一个新的节点,其父节点为 u ,权值为 val
- 2 u val:询问以节点 u 为起点的最长不下降子序列的长度,这里规定的最长不下降子序列需要满足以下几个条件:
- 以 u 为起点,每次的路线必须都是当前节点的父节点
- 序列中的权值和小于等于 val
题目分析:因为操作二需要每次按照要求往祖先节点上跳,我们不妨设计一下倍增来处理所需要解决的问题
- dp[ u ][ i ]:代表节点 u 向上经过 2^i 个权值大于等于 w[u] 的结点后的位置
- sum[ u ][ i ]:代表节点 u 向上经过 2^i 个权值大于等于 w[u] 后的权值和(只统计这 2^i 个数)
有了这两个数组的定义后,那么我们在插入时,可以logn维护一下这两个数组,在查询时,也可以logn获得答案,有很多细节,具体实现看代码吧:
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<cmath>
#include<sstream>
#include<unordered_map>
using namespace std;
typedef long long LL;
const LL inf=0x3f3f3f3f3f3f3f3f;
const int N=4e5+100;
LL last,dp[N][25],sum[N][25],w[N],cur;
void add(LL u,LL val)
{
u^=last;
val^=last;
w[++cur]=val;
if(w[cur]<=w[u])//处理dp[u][0]
dp[cur][0]=u;
else
{
for(int i=20;i>=0;i--)
if(w[cur]>w[dp[u][i]])
u=dp[u][i];
dp[cur][0]=dp[u][0];
}
if(dp[cur][0]==0)//处理sum[u][0]
sum[cur][0]=inf;
else
sum[cur][0]=w[dp[cur][0]];
for(int i=1;i<=20;i++)//维护dp数组和sum数组
{
dp[cur][i]=dp[dp[cur][i-1]][i-1];
if(dp[cur][i]==0)
sum[cur][i]=inf;
else
sum[cur][i]=sum[cur][i-1]+sum[dp[cur][i-1]][i-1];
}
}
LL query(LL u,LL val)
{
u^=last;
val^=last;
if(w[u]>val)
return 0;
LL ans=1;
val-=w[u];
for(int i=20;i>=0;i--)//统计答案
{
if(val>=sum[u][i])
{
val-=sum[u][i];
ans+=(1<<i);
u=dp[u][i];
}
}
return ans;
}
void init()//初始化sum数组到所有不可能到达的位置为无穷大
{
w[0]=inf;
w[1]=0;
last=0;
cur=1;
for(int i=0;i<=20;i++)
sum[1][i]=inf;
}
int main()
{
// freopen("input.txt","r",stdin);
// ios::sync_with_stdio(false);
init();
int n;
scanf("%d",&n);
while(n--)
{
LL op,p,q;
scanf("%lld%lld%lld",&op,&p,&q);
if(op==1)
add(p,q);
else
printf("%lld\n",last=query(p,q));
}
return 0;
}