最暴力的想法自然是,对于每一次询问,沿着父亲一点一点往上跳模拟。这样似乎并不能优化...
稍微好一点的想法是对于每一个点开一个nxt数组表示父亲中权值大于他的里面离他最近的在哪里,同样对于每一次询问模拟往上跳,每次加点模拟往上找,复杂度依旧没有变化,但为我们提供了一个思路,我们可以倍增啊!
首先我们对于2操作,我们记录sum[x][i]表示他的2^i个nxt的值的和是多少,这样我们就可以快速求出在总权值小于x的前提下最多能跳到哪里。
考虑如何求出这个数组。
定义nxt[x][i]为x的第2^i个nxt是谁。
对于每一次加点操作,我们记录fa[x][i]表示x的第2^i个父亲是谁,同时记录这些父亲中的最大值是多少。那么我们加入一个点时就可以快速求出第一个大于他的位置在哪里。即nxt[x][0],然后递推可以分别求出nxt[x][1],nxt[x][2].....沿途记录sum即可。
1 #include<bits/stdc++.h> 2 #define LL long long 3 using namespace std; 4 const int inf=4e5+10; 5 int Q,siz,last; 6 int w[inf]; 7 int fa[inf][20],Max[inf][20]; 8 int nxt[inf][20]; 9 LL sum[inf][20]; 10 void get(int x,int y){ 11 fa[x][0]=y; 12 Max[x][0]=w[y]; 13 for(int i=1;i<=19;i++){ 14 fa[x][i]=fa[fa[x][i-1]][i-1]; 15 Max[x][i]=max(Max[x][i-1],Max[fa[x][i-1]][i-1]); 16 } 17 int now=x; 18 for(int i=19;i>=0;i--){ 19 if(Max[now][i]<w[x])now=fa[now][i]; 20 } 21 nxt[x][0]=fa[now][0]; 22 sum[x][0]=w[fa[now][0]]; 23 for(int i=1;i<=19;i++){ 24 nxt[x][i]=nxt[nxt[x][i-1]][i-1]; 25 sum[x][i]=sum[x][i-1]+sum[nxt[x][i-1]][i-1]; 26 } 27 } 28 int p[20]; 29 int work(int x,LL y){ 30 if(w[x]>y)return 0; 31 int cnt=1; 32 y-=w[x]; 33 for(int i=19;i>=0;i--){ 34 if(nxt[x][i]!=0&&sum[x][i]<=y){ 35 y-=sum[x][i]; 36 x=nxt[x][i]; 37 cnt+=p[i]; 38 } 39 } 40 return cnt; 41 } 42 int main() 43 { 44 scanf("%d",&Q); 45 siz=1; 46 p[0]=1; 47 for(int i=1;i<=19;i++)p[i]=p[i-1]<<1; 48 while(Q--){ 49 int opt; 50 LL x,y; 51 scanf("%d%lld%lld",&opt,&x,&y); 52 x=x^last;y=y^last; 53 if(opt==1){ 54 siz++; 55 w[siz]=y; 56 get(siz,x); 57 } 58 else { 59 last=work(x,y); 60 printf("%d\n",last); 61 } 62 } 63 return 0; 64 }