[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\n",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,初中数学,相信就不用解释了

 

转载于:https://www.cnblogs.com/Danzel-Aria233/p/7646916.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
NOIP(全国青少年信息学奥林匹克竞赛)是中国国内最高水平的信息学竞赛之一,是计算机领域的重要赛事。针对NOIP模拟题,通常是为了帮助参赛者熟悉比赛形式和题型,提供足够的训练机会。 数据下载是NOIP比赛中的一个重要环节,因为参赛者需要根据提供的数据集进行程序开发和测试。数据下载一般通过网络进行,参赛者需要在指定的时间段内下载数据集到自己的计算机上。 在进行NOIP模拟题数据下载时,我们可以按照以下步骤进行操作: 1. 确认下载链接:NOIP官方会提供下载数据集的链接或者FTP地址,参赛者需要确认链接是否可用和正确。 2. 选择下载工具:根据自己的需求,参赛者可以选择合适的下载工具进行操作。常见的下载工具有浏览器内置下载工具、迅雷、IDM等,可以根据个人的习惯和需求选择合适的下载工具。 3. 打开下载工具:根据所选择的下载工具类型,打开对应的软件,进入下载界面。 4. 输入下载链接:将NOIP提供的数据集下载链接复制粘贴到下载工具的链接输入框中,点击确定或开始进行下载。 5. 等待下载完成:根据数据集的大小和网络速度不同,下载时间会有所变化。参赛者需要耐心等待下载完成,确保数据集完整地保存到自己的计算机上。 6. 验证数据完整性:下载完成后,参赛者需要验证数据集的完整性,确保所有文件都成功地保存到指定位置。可以进行文件大小的比对或者逐个文件的校验来检查数据完整性。 通过以上步骤,参赛者可以成功地进行NOIP模拟题数据的下载。在实际比赛中,一个高效的数据下载过程可以提高参赛者的准备时间和竞争力,确保能够充分利用所提供的数据集进行开发和测试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值