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;
}

浙公网安备 33010602011771号