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
",query(1,maxn,1));
32         }
33     }
34     return 0;
35 }
原文地址:https://www.cnblogs.com/acgoto/p/9267487.html