goj 3n+1问题(克拉兹问题+线段树区间查询最大值)
Problem Description:
考虑如下的序列生成算法:从整数n开始,如果n是偶数,把它除以2;如果 n 是奇数,把它乘3加1。用新得到的值重复上述步骤,直到 n = 1 时停止。例如,n = 22 时该算法生成的序列是: 22,11,34,17,52,26,13,40,20,10,5,16,8,4,2,1 。
人们猜想(没有得到证明)对于任意整数 n,该算法总能终止于 n = 1。这个猜想对于至少 1 000 000内的整数都是正确的。对于给定的 n,该序列的元素(包括 1)个数被称为 n 的循环节长度。在上述例子中,22 的循环节长度为 16。输入两个数 i 和 j,你的任务是计算 i 到 j(包含 i 和 j)之间的整数中,循环节长度的最大值。
Input:
输入包含一个整数m(1≤m≤10^5),表示测试组数,接下来的m行每行包含两个整数 i 和 j(0<i≤j≤50000)。
Output:
对于每对整数 i 和 j,输出二者之间的整数中的最大循环节长度。
Sample Input:
4 1 10 100 200 201 210 900 1000
Sample Output:
20 125 89 174
解题思路:打表预处理1~5×10^5每个数的循环节长度,然后建立线段树,区间查询(O(logn))即可,所以总的时间复杂度大概为O(nlogn)。
AC代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=50000; 4 int a,b,n,m,tmp,s[maxn+5],t[(maxn+5)<<2]; 5 void build(int l,int r,int x){ 6 int mid=(l+r)>>1; 7 if(l==r){t[x]=s[mid];return;} 8 build(l,mid,x<<1); 9 build(mid+1,r,x<<1|1); 10 t[x]=max(t[x<<1],t[x<<1|1]); 11 } 12 int query(int l,int r,int x){ 13 if(a<=l&&b>=r)return t[x];//如果该节点表示的区间恰好是要查询的区间,直接返回结果,即[l,r]是[a,b]的一个子集,直接返回最大值,不用继续往下查找 14 else{ 15 int mid=(l+r)>>1; 16 if(b<=mid)return query(l,mid,x<<1);//判断(编号)区间在哪棵子树上[a,b]在[l,r]的左子树[l,mid]上 17 else if(a>mid)return query(mid+1,r,x<<1|1);//[a,b]在[l,r]的右子树[mid+1,r]上 18 else return max(query(l,mid,x<<1),query(mid+1,r,x<<1|1));//表示[a,b],有一部分在[l,mid]上,有一部分在[mid+1,r]上,直接返回左右区间的最大值 19 } 20 } 21 int main(){ 22 for(int i=1;i<=maxn;++i){//预处理打表 23 tmp=i;n=1; 24 while(tmp!=1){tmp=(tmp%2)?3*tmp+1:tmp/2;++n;} 25 s[i]=n; 26 } 27 build(1,maxn,1);//建树 28 while(~scanf("%d",&m)){ 29 while(m--){ 30 scanf("%d %d",&a,&b); 31 printf("%d\n",query(1,maxn,1)); 32 } 33 } 34 return 0; 35 }