Segment Tree with Pruning HDU6983

HDU6983

题目链接 Segment Tree with Pruning

题目大意

​ 给定n,k,建造出一个最短区间长度为k的线段树,求创建过程中产生点的个数

​ 通过给定的函数,我们很容易得出节点个数的递推式。

​ 设\(f(n,k)\)为给定n,k的值,则有递推式:

\[f(n,k)=1,n<=k \\ f(n,k)=2*f(n/2,k)+1,n为偶数 \\ f(n,k)=f(\frac{n+1}{2},k)+f(\frac{n-1}{2},k),n为奇数 \]

​ 显然可以通过递归解决,但显然递归时大量的重复计算会让复杂度相当的不友好,而递推显然不好处理,我们考虑记忆化搜索,\(10^{12}\)​​的大量数据不可能通过开数组解决,所以使用\(map\)​存储。由于在递归过程中不断的对\(n\)​进行除2运算,所以产生节点数量级为\(log_2n\)​​的,复杂度为\(O(\log n\log\log n)\)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<string.h>
#include<cmath>
#include<deque>
#include<map>
#include<vector>
#define Lint  long long
using namespace std;
int T;
Lint n,k;
map<Lint,Lint>mp;
Lint f(Lint x,Lint y){
    if(x<=y) return 1;
    if(mp[x]) return mp[x];
    if(x&1) mp[x]=f((x+1)>>1,y)+f((x-1)>>1,y)+1;//善用位运算,减小常数
    else mp[x]=f(x>>1,y)*2+1;
    return mp[x];
}
int main(){
    scanf("%d",&T);
    while(T--){
        mp.clear();
        scanf("%lld%lld",&n,&k);
        printf("%lld\n",f(n,k));
    }
    return 0;
}
posted @ 2021-07-28 14:07  wzyyy  阅读(107)  评论(0)    收藏  举报