トピックリンク:こちらをクリックしてください
タイトル効果:指定された木、最初に一つだけのノード1,0の重量は、n個の各動作は2例に分割され、フォローアップ操作を有します。
- 1 Uヴァル:挿入ツリーに新しいノード、親ノードは、U、ヴァルの重量であります
- 2 UのVal:最長クエリー配列の長さは、Uはアップ本明細書中、以下の条件を満たしている必要があり、所定のドロップ配列に、出発点であるノードに低下しません。
- Uで始まる点として、各ルートなければならない現在のすべてのノードの親
- 体重および以下のシーケンスヴァル
トピック分析:操作は要件に応じて、それぞれが祖先ノードにジャンプ2を必要とするため、我々は、設計時に見えるかもしれません、解決すべき問題に対処するために倍増
- DP [U] [I]:代表ノードの位置は、2 ^ I U 2上方重量Wの後に[U]ジャンクションに等しいより大きい
- 和[U] [I]:代表ノードuの上方を通って2 ^ I番目の加重値に等しい重みW [U]と(2 ^ Iれるのみ統計数)
私たちが挿入されたとき、それは、クエリでは、2つの配列を維持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;
}