zoukankan      html  css  js  c++  java
  • 【等比数列】序列

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


    背景

    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;
    }
    

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

  • 相关阅读:
    mysql 8 查询报错(sql_mode=only_full_group_by)
    docker安装mysql8版本后 客户端连接出现client does not support authentication...
    docker常用命令
    查看tomcat日志相关Linux命令
    java项目部署到linux服务器涉及的命令
    ehcache与redis对比
    JS中调用BigDecimal处理金额
    thymeleaf模板 th:href 踩坑
    汇总一些绝对有价值的解决方案,边学习边收集
    spring注解总结,spring注解大全
  • 原文地址:https://www.cnblogs.com/qxyzili--24/p/11188088.html
Copyright © 2011-2022 走看看