展览

    • 题目描述
    • 输入
    • 输出
    • 样例输入
    • 样例输出
    • 提示
      • 证明一组数据是某等比数列的子序列
      • 那么什么时候说明不是某等比数列的子序列?
    • Code

题目描述

小R的家里种着一排花,现在她想选出其中连续的一段拿去展览。
每朵花都有一个高度ai,选出的一段必须满足:将这一段花的高度从小到大排序后,公比是某个正整数q(q的最大质因子≤1000)的等比数列的子序列。
机智的你能不能帮小R看一看,最多能选出多少朵花。

输入

输入第一行一个正整数n,表示有n朵花(n≤10000)。
接下来n行,每行一个正整数ai,其中a1,a2,⋯an依次表示n朵花的高度。

输出

输出一个整数,表示最长的符合要求的连续一段的长度。

样例输入

10
1
2
3
4
5
6
7
8
9
10

样例输出

2

提示

样例只有1,2这个子段满足条件,长度为2。

关于这个题,首先可以确定的是,ans<=65,因为ans是一个等比数列的子序列,那么由于long long int的限制,ans<=65。(说到这里跟没说一样,,,)

这个题的关键问题在于怎么论证一组数据是不是某等比数列的子序列。。(废话)

证明一组数据是某等比数列的子序列

假设这个组数据是 a1,a2,a3,...an a 1 , a 2 , a 3 , . . . a n ,(从小到大)那么首先需要保证的是 ai+1 a i + 1 % ai==0 a i == 0
另外的一组数据 x1,x2,x3,...xm x 1 , x 2 , x 3 , . . . x m (m = n - 1)由上面的数据生成,其中 xi x i = ai+1 a i + 1 / ai a i
这时 xi x i 必然可以表示成 qpi q p i (其中q是公比)
那么这个时候,我们得到了一组数据 p1,p2,p3,...,pm p 1 , p 2 , p 3 , . . . , p m
注意,上面是铺垫,重点来了
假如说任意的 xi x i 也就是 qpi q p i ,都可以表示成 (qy)ji ( q y ) i j ,通俗的讲,就是 pi=yji p i = y j i ,y是固定的值,那么可以说这组数据是等比数列的子序列

上面那句话可能很绕,但我觉得很重要,实际上对于整个问题,只要求出公比q就可以了,但是我们不能求出这个最小的q,只是有可能求出q的整数次方,但这个q的整数次方可能也符合题意。出现这种问题,主要是因为某等比数列不唯一,
试想如果可以看成公比是4的某等比数列子序列,那么可以看成公比为2的子序列。

那么问题到这里其实就相当于求 p1,p2,p3,...,pn p 1 , p 2 , p 3 , . . . , p n 这组数据的最大公约数y。当然可能这个y可以继续分解,但只要求出y即可。

那么问题到这里相当于什么还是没说,因为不可能求出 y=gcd(p1,p2,p3,...,pn) y = g c d ( p 1 , p 2 , p 3 , . . . , p n ) ,但是实际上可以求出 qy q y ,至于怎么求这个,毕竟那是指数。但首先还有一点显然的死 pi<=35 p i <= 35

如果还记得欧几里得算法怎么写的话就好办了。

——来自百度百科
unsigned int Gcd(unsigned int M,unsigned int N)
{
    unsigned int Rem;
    while(N > 0)
    {
        Rem = M % N;
        M = N;
        N = Rem;
    }
    return M;
}

可以看到里面除了赋值运算,就是取模运算,现在问题在于,指数怎么取模。毕竟两个数相除等于指数相减。
但实际上,我们就来模拟两个数相减来取模。5%2=5-2-2=1,就这样。
可能这样复杂度比较高,但别忘了,那是指数,很小。
那么这个过程就是 qa q a 不断的除以 qb q b 一直到不能除为止。那么此时得到的 qc=qamodb q c = q a m o d b
基本就是这样。

那么什么时候说明不是某等比数列的子序列?

注意,上面再模拟取模的过程中,始终是以q为底数的,所以必然是只要a大,a肯定能整除b,那么不能整除的时候肯定return false;

Code

#include 

using namespace std;
const int maxn = 100000 + 10;
typedef long long int ll;
ll d[60000];
int tot;
ll a[maxn];
ll b[70];
ll x[70];

ll anotherMod(ll a, ll b) {
    while (a % b == 0 && b != 1) {//因为b如果等于1,相当于x^p中的p == 0,此时不能取mod
        a /= b;
    }
    return a;
}

ll anotherGcd(ll a, ll b) {
    ll r;
    while (b > 1) {//b不能等于1理由同上
        r = anotherMod(a, b);
        if (r > b) return -1;//如果r比b大,但是r不能整除b,此时就不行了
        a = b;
        b = r;
    }
    return a;
}

int prime[] = {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};

bool Judge(int l, int r) {
    if (l == r) return 1;
    int 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 == 0) 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];
        if (x[i - 1] == 1) return 0;//x是比例,如果x == 1,说明公比为1,但是之前公比为一的已经返回,所以矛盾
    }
    ll gcdAns = x[0];
    for (int i = 1; i < len - 1; i++) {
        gcdAns = anotherGcd(gcdAns, x[i]);
        if (gcdAns == -1) return 0;
    }
    if (gcdAns <= 1000) return 1;
    for (int i = 0; prime[i] != -1; i++) {
        if (gcdAns % prime[i] == 0) return 1;
    }
    return 0;

}


int main() {
    int n, ans = -1;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%lld", &a[i]);
    }
    int l = 0, r = 0;
    int N = n << 1;
    while (l < N) {//某大佬写的双指针
        if (Judge(l, r)) {
            ans = max(ans, r - l + 1);
            r++;
            if (r == n) {
                r--;
                l++;
            }
        } else {
            l++;
        }
    }
    printf("%d\n", ans);
    return 0;
}


/**************************************************************
    Language: C++
    Result: 正确
    Time:60 ms
    Memory:2952 kb
****************************************************************/

你可能感兴趣的:(数论)