【等比数列】序列
题目如上,样例极水。。。。。
背景
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; }
完了完了,皆大欢喜!!!