【等比数列】序列

题目如上,样例极水。。。。。


背景

2019.7.14

  sdfz某蒟蒻选手参加了hzoj上的NOIP模拟测试3.

  然后, 爆零了。

  真是个悲伤的故事。。。。。

  本来我算下复杂度是差不多是对的,最次可能就是TLE了。

  结果是全部WA了。

  ??????????????

  ??????????????

  ??????????????

  发生了什么?


考场分析

  1. 这是一条由等比数列的其中一部分数随机排列而成的数列。

  2. 这条数列是没有重复的数字的。

  3.公比可能为 1。

  4.这段序列的公比的值非常小 q<=1000

 联想一下数学的解题思路,就可以想到:

    判断几个数是不是能够构成等比数列,就是一个反复取模的问题。

 那么举个例子来证明一下:

    假设这里有3个数 a=a1,b=a1*q,c=a1*q2*b

    那么如果让a与b取模,就会发现,只要a,b呈现倍数关系就一定会得到0

    所以我们这个时候可以取出b/a的值,记为x

      x=b/a,这个时候的x就是这个等比数列的公比的某次方

    同样,我们对于b,c也重复这样的过程,得到y=c/b

    接下来就是将a与c的关系建立起来了。

 那么接下来是不是要取a和c的模呢?

    答案当然是否定的,【不然如果数字的数量多了的话怎么办啊?

    先感性猜测一下,如果是把x与y取模的话,会发生什么呢?

    首先,a和c都是可以用b来表示出来的

    那么x和y也是可以在同乘一个数之后得到b的,

    那么x和y就相当于是a和c对于b的一个 映射?或者说叫代表?

 所以自然结论就是采取x和y取模的方法。

    还是刚刚那个例子,将x和y取模.

    如果得到0,那么x与y呈现 xk=y 的结果,自然就是三个数呈现等比关系

    如果不是那么肯定不是。

 故而可以枚举第一个数的位置,将它作为a,对于之后的数进行枚举处理。


成熟思路

  俗话说的好啊,有了小思路,就一定能向大方向扩展。 【雾

  刚刚讲到要反复取模,这个时候就可以发现,其实是有冗余的部分的。 

  仔细想想会发现,假如一个数列特别长,然后这种数列还特别多

  那么对于一个数来说有可能会反反复复计算好几回。

  所以肯定不是正解。【NOIP模拟赛怎么可能是一道模拟题??

   对于题面的分析的时候我们发现,公比可能是一个素数,也可能不是。

 

  然后再次回到取模的问题上。

  既然是NOIP模拟赛,怎么可能不考一种算法呢?【强词夺理

细细回顾之前的算法,有哪个是反复取模的过程?

  emmmm......

  

  bingo!gcd!

  可是gcd和取公比又有什么关系呢?

  ????????????????????????

  ????????????????????????

  ????????????????????????

  开始暴躁。。。。

  这个时候我们不妨仔细回顾一下辗转相除法,

  

  发现了嘛?

  是不是和我们刚才取公比的过程惊人的相似?

那么结论就出现了,可以优化求公比的过程。

ll hgcd(ll a,ll b) {
    ll r;
    while(b>1) {
    	while(!(a%b)&&b!=1)
    		a/=b;
        r=a;
        if(r>b) 
	  return -1;
        a=b;
        b=r;
    }
    return a;
}

 

  那么既然找到公比,我们就可以直接向后判断了啊。

  当然找到的公比很可能还能分解,但是这里就不需要了。


细节注意

  1. 期间要多次注意公比为1的情况。

   2.  % 后面的数字不能比前面的大,不然就算是倍数也会得到值。

   3. 素数筛一定要写对!!!!!!!!!!!!!!!!不然还不如copy一个素数表

 

   4. 对于怎么应用上面的部分,直接双指针枚举 l,粘出来一段进行上面的操作就ok了。

 


 

代码实现

  我知道你们只看这个。。。。。

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath> 
#include<algorithm>
#define ll long long 
using namespace std;

int n,ans=0;
int tot;
ll a[100010],b[70],x[70],d[60000];
int prime[1001]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,
	           107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,
	           227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,
	           349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,
	           467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,
	           613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,
	           751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,
	           887,907,911,919,929,937,941,947,953,967,971,977,983,991,997,-1};

ll hgcd(ll a,ll b) {
    ll r;
    while(b>1) {//%后的数必须小 
    	while(!(a%b)&&b!=1)
    	  a/=b;
        r=a;
        if(r>b) 
	  return -1;
        a=b;
        b=r;
    }
    return a;
}

bool pd(int l,int r) {
    if(l==r) 
	return 1;
    bool flag=0;
    for(int i=l;i<=r;i++) {
        b[i-l]=a[i];
        if(i!=l&&a[i]!=a[i-1]) 
	  flag=1;
    }
    if(!flag) 
	return 1;//如果公比为1
    int len=r-l+1;
    sort(b,b+len);//注意复制的时候没有改变位置 
    for(int i=1;i<len;i++) {
        if(b[i]%b[i-1]!=0) 
		return 0;
        x[i-1]=b[i]/b[i-1];//x是公比数组 
        if(x[i-1]==1) 
	  return 0;//如果x==1,说明公比为1
    }
    ll Gcd=x[0];
    for(int i=1;i<len-1;i++) {
        Gcd=hgcd(Gcd,x[i]);
        if(Gcd==-1) 
	  return 0;
    }
    for(int i=0;prime[i]!=-1;i++)
        if(!(Gcd%prime[i])) //筛 
		return 1;
    return 0;

}

int main() 
{
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%lld",&a[i]);//从头存方便筛/复制 
    int l=0,r=0;
    while(l<(n<<1)) {
        if(pd(l,r)) {
            ans=max(ans,r-l+1);
            ++r;
            if(r==n) {
                --r;
                ++l;
            }
        } 
    else
            ++l;
    }
    cout<<ans<<endl;
    return 0;
}

 

  完了完了,皆大欢喜!!!

 

posted @ 2019-07-15 15:41  鸽子咕  阅读(617)  评论(0编辑  收藏  举报