题意:一颗满二叉树n(1e6)个顶点,边权都知道,一共q(1e5)次询问.从ST点出发有生命值H,求dis(st,任意一点x)<=H 的 sgima(H-dis)
思路:
1.对于一个点,出发有三种情况,左儿子/右儿子/父亲,求是否能走到 以及 走到后有多少个可能的值(二分)
2.那么从ST出发往左右儿子走的情况我们能都处理出来,但是往上走的我们还没处理. 再想,ST/2就到父亲来了,那岂不是h-cost[father]就是当前剩余的生命值了吗? 如果前面是左儿子过来的,只检查father的rson. 如果是右儿子爬过来的,那么只检查father的lson.
画图理解一下! 其实模拟一下,观察是可以做的?
时间复杂度:[n(1+1/2+1/4+...)=O(n) ]+ O(nlogn * log(logn))+O(q*logn*log(logn))
这道题还要注意空间复杂度: nlogn n=1e6. 正常开会爆内存,开vector优化空间
#include<bits/stdc++.h>
#define PI acos(-1.0)
using namespace std;
typedef long long ll;
const int N=1e6+6;
const int MOD=1e9+7;
const int INF=0x3f3f3f3f;
vector <int> ans[N];
vector <ll> sum[N];
int cost[N],h;
int n,q,st,last;
/// distance bewteen subtree root and subtree node
void dfs(int now,int rt,int fdis){
if(now>n) return ;
int lson=now*2,rson=now*2+1;
if(lson<=n) ans[rt].push_back(cost[lson]+fdis),dfs(lson,rt,cost[lson]+fdis);
if(rson<=n) ans[rt].push_back(cost[rson]+fdis),dfs(rson,rt,cost[rson]+fdis);
}
int main(void){
cin >> n>>q;
for(int i=2;i<=n;i++) scanf("%d",&cost[i]);
for(int i=1;i<=n;i++) // pb(0)是为了符合平时写前缀和的习惯,把VEC第0个位置先占了,dfs是用来求当前节点到字节点的ALLDistans
ans[i].push_back(0),dfs(i,i,0);
for(int i=1;i<=n;i++){
sort(ans[i].begin(),ans[i].end());
sum[i].resize((int)ans[i].size()+1,0);
for(int j=1;j<(int)ans[i].size();++j){
sum[i][j]+=sum[i][j-1]+1ll*ans[i][j];
}
}
// for(int i=1;i<=n;i++){
// printf("%d: ",i);
// for(auto j : ans[i]) printf("%lld ",j);
// cout<<endl;
// for(auto j : sum[i]) printf("%lld ",j.second);puts("*************");
// }
for(int i=1;i<=q;i++){
scanf("%d%d",&st,&h);
ll res=0;
while(st>0 && h>0){
res+=1ll*h;
int lson=st*2;
int rson=st*2+1;
if(last!=lson && lson<=n){
int te=h-cost[lson];
// cout <<"te="<<te<<endl;
if(te>0){
res+=te;
// cout <<"te1="<<te<<endl;
int pos=prev(lower_bound(ans[lson].begin(),ans[lson].end(),te))-ans[lson].begin();
// cout <<"pos="<<pos<<endl;
res+= (1ll*pos*te-sum[lson][pos]);
}
// cout <<"rse1="<<res<<endl;
}
if(last!=rson && rson<=n){
int te=h-cost[rson];
if(te>0){
res+=te;
int pos=prev(lower_bound(ans[rson].begin(),ans[rson].end(),te))-ans[rson].begin();
res+= (1ll*pos*te-sum[rson][pos]);
}
// cout <<"rse2="<<res<<endl;
}
last=st;
h-=cost[last];
// cout <<"h="<<h<<endl;
st/=2;
// cout <<"st="<<st<<endl;
}
cout << res << endl;
}
return 0;
}
/*********
6 4
2
1
1
3
2
1 3
*********/