zoukankan      html  css  js  c++  java
  • [noip模拟]数字对<RMQ&二分>

    数字对

    【题目描述】

    H是个善于思考的学生,现在她又在思考一个有关序列的问题。

    她的面前浮现出一个长度为n的序列{ai},她想找出一段区间[L, R](1 <= L <= R <= n)

    这个特殊区间满足,存在一个k(L <= k <= R),并且对于任意的i(L <= i <= R)ai都能被ak整除。这样的一个特殊区间 [L, R]价值为R - L

    H想知道序列中所有特殊区间的最大价值是多少,而有多少个这样的区间呢?这些区间又分别是哪些呢?你能帮助她吧。

    【输入格式】

    第一行,一个整数n.

    第二行,n个整数,代表ai.

    【输出格式】

    第一行两个整数,numval,表示价值最大的特殊区间的个数以及最大价值。

    第二行num个整数,按升序输出每个价值最大的特殊区间的L.

    【样例输入1】

    5

    4 6 9 3 6

    【样例输出1】

    1 3

    2

    【样例输入2】

    5

    2 3 5 7 11

    【样例输出2】

    5 0

    1 2 3 4 5

    【数据范围】

    30%: 1 <= n <= 30 , 1 <= ai <= 32.

    60%: 1 <= n <= 3000 , 1 <= ai <= 1024.

    80%: 1 <= n <= 300000 , 1 <= ai <= 1048576.

       100%: 1 <= n <= 500000 , 1 <= ai < 2 ^ 31.

    【思路】

    题不是很难,就是可能一些细节注意不到或者优化想不到

    我拿到题后的第一反应是一个数组f[i][j][k]表示从i开始的j位来记录这个区间的最小值和最大公约数

    随便举一些例子就不难发现,其实要满足条件的区间,整除的那个数是这个区间最小而且是最大公约数

    那么题就简单了,就是求最小值和最大公约数相同的区间,然后找到区间最大的并输出左端点

    我们之前定义了一个f[i][j][k],然后结合一起的知识,我们不难有一个大胆的想法

    就是用RMQ来处理这个小小细节,表示为gcdn[i][j]是从i位开始的2^j位这个区间的最大公约数

                       minn[i][j]是从i为开始的2^j位这个区间的最小值

    这里一想出来,接下来就是区间的最大长度了。。。这个简单,枚举一下这个长度然后找gcd和min相同的区间

    当然,枚举,不存在的,直接二分就行了。。。。另外一提,这题非常容易超时

    所以我来提几个细节

    1.在RMQ时的外循环,不能循环到int类型的边界,而是要根据n来定义,循环到log₂n次(注意不要搞成根号n了),j是代表2^j次方

    2.在表示2^j次方不要直接用函数pow,要用位运算,位运算的时间消耗更小。。。

    只有这样才不会超时

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<iostream>
     4 #include<cstdlib>
     5 #include<cmath>
     6 #include<cstring>
     7 #include<queue>
     8 #define maxn 500005
     9 using namespace std;
    10 int gcdn[maxn][32],minn[maxn][32],n,m,a[maxn];
    11 int gcd(int a,int b){
    12     if(b==0)return a;
    13     else return gcd(b,a%b);
    14 }
    15 void rmq(){
    16     for(int j=1;j<=(int)log2(n);j++){
    17         for(int i=1;i<=n;i++){
    18             int new_=pow(2.0,j)+i-1;
    19             if(new_>n)continue; 
    20             minn[i][j]=min(minn[i][j-1],minn[i+ (1<<(j-1))][j-1]);
    21             gcdn[i][j]=gcd(gcdn[i][j-1],gcdn[i+ (1<<(j-1))][j-1]);
    22         }
    23     }    
    24 }
    25 int ans[maxn],num,val,len;
    26 void change(int l,int r,int n){
    27     if(l==r)return;
    28     int mid=(l+r)>>1;
    29     int j=log2(mid+1);
    30     for(int i=1;i<=n-mid;i++){
    31         int a1,a2;
    32         a1=min(minn[i][j],minn[i+mid- (1<<j) + 1][j]);
    33         a2=gcd(gcdn[i][j],gcdn[i+mid- (1<<j) + 1][j]);
    34         if(a1==a2){
    35             num++;ans[num]=i;
    36         }
    37     }
    38     if(num==0)change(l,mid,n);
    39     else{
    40         val=mid;len=num;
    41         num=0;change(mid+1,r,n);
    42     }    
    43 }
    44 int main(){
    45     scanf("%d",&n);
    46     for(int i=1;i<=n;i++){
    47         scanf("%d",&a[i]);
    48         gcdn[i][0]=minn[i][0]=a[i];
    49     }
    50     rmq();
    51     change(0,n-1,n);
    52     printf("%d %d
    ",len,val);
    53     for(int i=1;i<=len;i++){
    54         printf("%d ",ans[i]);
    55     }
    56 } 
    View Code

    总结:

    1.对于区间的最值一类问题,可以用RMQ解决

    2.pow函数的使用要注意pow出来的值是浮点数,而且pow函数里的第一个数必须是浮点数,比如要求2^j,就是pow( 2.0, j ),当然这个细节是因为cena里会编译错误,自己是不会报错的

    3.能用位运算就尽量用位运算,特别是循环中会多次执行一个语句,换成位运算后可以大大省下时间

    4.注意区分一下log2和sqrt,两个出来的值不同,比如log2(64)=6,sqrt(64)=8,初中数学,相信就不用解释了

  • 相关阅读:
    atitit,it人怎么样才容易事业成功?? 有以下五种性格的人容易成功
    atitit。win7 win8 win9 win10 win11 新特性总结与战略规划
    atitit.提升兼容性最佳实践 o9o
    atitit.attilax.com产品 软件项目通用框架类库总结
    atitit. 文件上传带进度条 atiUP 设计 java c# php
    atitit.窗体静听esc退出本窗体java swing c# .net php
    atitit.提升研发管理的利器---重型框架 框架 类库的区别
    atitit.导出excel的设计----查询结果 导出为excel的实现java .net php 总结
    atitit. java跟php的比较..为什么大企业喜欢java 而不是php
    atitit. java jsoup html table的读取解析 总结
  • 原文地址:https://www.cnblogs.com/Danzel-Aria233/p/7646916.html
Copyright © 2011-2022 走看看