首页 > 代码库 > 某次模拟赛 数字对

某次模拟赛 数字对

题目描述 Description

小 H 是个善于思考的学生,现在她又在思考一个有关序列的问题。
她的面前浮现出一个长度为 n 的序列{ai},她想找出一段区间[L, R](1 <= L <= R <= n)。这个特殊区间满足,存在一个 k(L <= k <= R),并且对于任意的 i(L <= i <= R),ai 都能被 ak 整除。这样的一个特殊区间 [L, R]价值为 R - L。
小 H 想知道序列中所有特殊区间的最大价值是多少,而有多少个这样的区间呢?这些区间又分别是哪些呢?你能帮助她吧。

输入描述 Input Description

第一行,一个整数 n.
第二行,n 个整数,代表 ai.

输出描述 Output Description

第一行两个整数,num 和 val,表示价值最大的特殊区间的个数以及最大价值。
第二行 num 个整数,按升序输出每个价值最大的特殊区间的 L.

样例输入 Sample Input

5
4 6 9 3 6

样例输出 Sample Output

1 3
2

数据范围及提示 Data Size & Hint

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.

之前的一些废话:暑假作业发现还有论语视频没有看,十分慌张

题解:传说中的暴力踩标程。

解法1:先说正解吧,首先最大价值是可以进行二分的,如何判断解是否可行呢?当前解是mid,枚举每一个长度为mid+1的区间,判这个区间是否合法,如果合法就把该区间左端放入队列中,最后检查队列中是否有元素即可(即有没有符合的区间)至于如何判区间是否合法,根据题意只要区间最小值与区间GCD相等就可以,这个可以通过RMQ来处理,O(n log n)预处理,O(1)查询,至于区间GCD通过类似的方法处理即可。    总复杂度O(n log n*log n)  

解法2:再说暴力。对于序列中每一个元素进行考虑,假定当前数为ak,那么我们需要计算出ak所属于的最长区间。向左向右分别扫,如果碰到一个数不能整除ak就停,最后计算出该区间长度[L,R]。然后还有一个优化,处理完第i个数后,我们没有必要继续处理第i+1个数,而是直接从第I个数所对应的R的下一个数开始处理即可,因为我们可以证明,从第i+1个数到第R个数中选它们做ak的答案显然没有拿第i个数当ak更优。复杂度O(♂)

解法3:还有一种类似暴力的做法,该做法与暴力做法的区别就是ak不用往左往右扫了,而是通过二分来确定区间长度,不过也需要区间GCD预处理。复杂度O(n log n*log n)

最后实测结果是解法2速度最快 ,解法1第二 解法3第三

代码:

解法1&&解法2的:

技术分享
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
#define mem(a,b) memset(a,b,sizeof(a))
inline int read()
{
    int x=0,f=1;char c=getchar();
    while(!isdigit(c)){if(c==-)f=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-0;c=getchar();}
    return x*f;
}
const int maxn=500010,MAXlog=20;
int n,a[maxn],f[maxn],ans,cnt,Min[MAXlog][maxn],GCD[MAXlog][maxn],len[maxn],L,R,A[maxn],l;
bool ok;
int gcd(int a,int b){return b==0 ? a : gcd(b,a%b);}
int MINquery(int L,int R)
{
    int bin=len[R-L+1];
    return min(Min[bin][L],Min[bin][R-(1<<bin)+1]);
}
int GCDquery(int L,int R)
{
    int bin=len[R-L+1];
    return gcd(GCD[bin][L],GCD[bin][R-(1<<bin)+1]);
}
bool check(int index)
{
    l=0;
    for(int i=1;i+index-1<=n;i++)if(GCDquery(i,i+index-1)==MINquery(i,i+index-1))A[l++]=i;
    if(l)ans=max(ans,index);
    return l;
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)Min[0][i]=GCD[0][i]=a[i]=read();
    for(int i=1;(1<<i)<=n;i++)
        for(int j=1;j+(1<<(i-1))<=n;j++)
        {
            Min[i][j]=min(Min[i-1][j],Min[i-1][j+(1<<(i-1))]);
            GCD[i][j]=gcd(GCD[i-1][j],GCD[i-1][j+(1<<(i-1))]);
        }
    for(int i=2;i<=n;i++)len[i]=len[i>>1]+1;
    L=1;R=n;
    while(R-L>1)
    {
        int mid=(L+R)>>1;
        if(check(mid))L=mid;
        else R=mid;
    }
    check(L);check(R);check(ans);
    printf("%d %d\n",l,ans-1);
    for(int i=0;i<l;i++)
    {
        if(!ok)printf("%d",A[i]),ok=1;
        else printf(" %d",A[i]);
    }
    printf("\n");
    return 0;
}
View Code
技术分享
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
#define mem(a,b) memset(a,b,sizeof(a))
inline int read()
{
    int x=0,f=1;char c=getchar();
    while(!isdigit(c)){if(c==-)f=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-0;c=getchar();}
    return x*f;
}
const int maxn=500010;
int n,a[maxn],f[maxn],ans,cnt;
bool ok;
int gcd(int a,int b){return b==0 ? a : gcd(b,a%b);}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)a[i]=read();
    for(int i=1;i<=n;i++)
    {
        int L=i,R=i;
        for(L=i;L>0;L--)if(a[L]%a[i]!=0)break;
        for(R=i;R<=n;R++)if(a[R]%a[i]!=0)break;
        L++;R--;
        f[L]=R-L;
        i=R;
    }
    for(int i=1;i<=n;i++)ans=max(ans,f[i]);
    for(int i=1;i<=n;i++)if(f[i]==ans)cnt++;
    printf("%d %d\n",cnt,ans);
    for(int i=1;i<=n;i++)
        if(f[i]==ans)
        {
            if(!ok)printf("%d",i),ok=1;
            else printf(" %d",i);
        }
    printf("\n");
    return 0;
}
View Code

解法3懒得写了。

总结:

某次模拟赛 数字对