• HZOI2019序列


    题目链接:https://www.cnblogs.com/Juve/articles/11186805.html(密码是我的一个oj用户名)

    题解:

    这题我考试打的暴力,只有5分。

    一开始理解错题意了,以为2,4,32这类不符合,于是有了下面的代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #define ll long long
     6 #define MAXN 100005
     7 #define re register
     8 #define in inline
     9 using namespace std;
    10 in ll read(){
    11     re ll x=0;char ch=getchar();
    12     while(ch<'0'||ch>'9'){ch=getchar();}
    13     while(ch>='0'&&ch<='9'){
    14         x=(x<<3)+(x<<1)+ch-'0';
    15         ch=getchar();
    16     }
    17     return x;
    18 }
    19 ll n,a[MAXN],tot=1,ans=1;
    20 in ll max(re ll a,re ll b){
    21     return a>b?a:b;
    22 }
    23 in ll min(re ll a,re ll b){
    24     return a<b?a:b;
    25 }
    26 in bool judge(re ll x,re ll y){
    27     re ll num=0,q;
    28     re ll b[MAXN];
    29     for(re ll i=x;i<=y;i++){
    30         b[++num]=a[i];
    31     }
    32     sort(b+1,b+num+1);
    33     //cout<<b[1]<<' '<<b[2]<<endl;
    34     //if(b[2]==b[1]) return 0;
    35     q=b[2]/b[1];
    36     for(re ll i=3;i<=num;i++){
    37         if(b[i]%b[i-1]!=0) return 0;
    38         if(b[i]==b[i-1]) return 0;
    39         if(b[i]/b[i-1]!=q) return 0;
    40     }
    41     return 1;
    42 }
    43 signed main(){
    44     //freopen("test.in","r",stdin);
    45     n=read();
    46     for(re ll i=1;i<=n;i++){
    47         a[i]=read();
    48         if(a[i]==1) ans=2;
    49         if(a[i]==a[i-1]) tot++;
    50         else{
    51             ans=max(tot,ans);
    52             tot=1;
    53         }
    54     }
    55     if(ans==n){
    56         printf("%lld
    ",ans);
    57         return 0;
    58     }
    59     for(re ll i=1;i<=n;i++){
    60         if(!a[i]||!a[i+1]){
    61             continue;
    62         }
    63         for(re ll j=i+2;j<=n;j++){
    64             //cout<<j<<endl;
    65             if(judge(i,j)){
    66                 //cout<<i<<' '<<j<<endl;
    67                 tot=j-i+1;
    68                 ans=max(ans,tot);
    69                 //cout<<tot<<endl;
    70             }
    71             else{
    72                 ans=max(ans,tot);
    73             }
    74         }
    75     }
    76     ans=max(ans,tot);
    77     printf("%lld
    ",ans);
    78     return 0;
    79 }
    5分

    考完试看正解,没看懂,于是开始更改我的暴力思路

    设dp[q][i]表示公比为q,以i结尾能组成题目中序列的个数

    我们先求出数列的max和min,得到q的范围

    首先枚举q,之后枚举整个数列,对与每个a[i]和a[i-1],如果max(a[i],a[i-1])%min(a[i],a[i-1]),

    那么枚举q的指数qp,如果min(a[i],a[i-1])*qp=max(a[i],a[i-1]),那么从i向前dp[q][i-1]中判重,若没有重复,那么dp[q][i]=dp[q][i-1]+1;

    判重是防止以下情况:4,2,4;

    4在前面出现过一次了,所以dp[q][3]=2而不是3;

    具体做法:

    for(ll p=1;p<=63;p++){
                        ll Q=power(q,p);
                        if(Q>maxx) break;
                        if(mi*Q==ma){
                            bool flag=0;
                            for(ll k=1;k<=dp[q][i-1];k++){
                                if(a[i]==a[i-k]){
                                    dp[q][i]=k;flag=1;
                                    ans=max(ans,dp[q][i]);
                                    //cout<<ans<<' '<<dp[q][i]<<' '<<q<<' '<<i<<endl;
                                    break;
                                }
                            }
                            if(!flag){
                                dp[q][i]=dp[q][i-1]+1;
                                ans=max(ans,dp[q][i]);
                                //cout<<ans<<' '<<dp[q][i]<<' '<<q<<' '<<i<<endl;
                            }
                            break;
                        }
                    }
    View Code

    细节的地方包括判断0以及公比是一的情况,随时更新ans

    Ps:因为这道题的数据实在是不好造,所以最后公比最大不会很大,博主试验过了,公比最大是10

    于是我们快乐地A掉了这道题

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define ll long long
    #define MAXN 100005
    #define re register
    #define in inline
    using namespace std;
    in ll read(){
        re ll x=0;char ch=getchar();
        while(ch<'0'||ch>'9'){ch=getchar();}
        while(ch>='0'&&ch<='9'){
            x=(x<<3)+(x<<1)+ch-'0';
            ch=getchar();
        }
        return x;
    }
    ll n,a[MAXN],tot=1,ans=0,maxx=0,minn=9e18;
    ll max_q,dp[15][MAXN];//dp[q][i]表示公比为q,以一下标i结尾的序列最大能得到多长的等比序列
    in ll max(re ll a,re ll b){
        return a>b?a:b;
    }
    in ll min(re ll a,re ll b){
        return a<b?a:b;
    }
    ll power(ll a,ll b){
        ll ans=1;
        for(;b;b>>=1){
            if(b&1) ans=(ll)ans*a;
            a=(ll)a*a;
        }
        return ans;
    }
    signed main(){
        n=read();
        //cout<<n<<endl;
        for(re ll i=1;i<=n;i++){
            a[i]=read();
            maxx=max(a[i],maxx);
            minn=min(a[i],minn);
            if(a[i]==0){
                ans=max(ans,tot);
                tot=1;
                continue;
            }
            if(a[i]==a[i-1]){ 
                tot++;
                ans=max(ans,tot);
            }
            else{
                ans=max(ans,tot);
                tot=1;
            }
            //cout<<tot<<endl;
        }
    //    cout<<ans<<endl;
        if(minn==0) minn=1;
        max_q=min(10,maxx/minn);
        //cout<<max_q<<endl;
        for(ll q=2;q<=max_q;q++){//枚举公比
            dp[q][0]=0;
            if(a[1]==0) dp[q][1]=0;
            else dp[q][1]=1;
            for(ll i=2;i<=n;i++){
                if(a[i]==0){
                    dp[q][i]=0;
                    continue;
                }
                ll ma=max(a[i],a[i-1]),mi=min(a[i],a[i-1]);
                //cout<<i<<endl;
                //cout<<ma<<' '<<mi<<endl;
                if(ma==mi){
                    dp[q][i]=1;
                    continue;
                }
                if(mi==0||ma%mi!=0){
                    dp[q][i]=1;
                    //cout<<ans<<' '<<dp[q][i]<<' '<<q<<' '<<i<<endl;
                }else{
                    for(ll p=1;p<=63;p++){
                        ll Q=power(q,p);
                        if(Q>maxx) break;
                        if(mi*Q==ma){
                            bool flag=0;
                            for(ll k=1;k<=dp[q][i-1];k++){
                                if(a[i]==a[i-k]){
                                    dp[q][i]=k;flag=1;
                                    ans=max(ans,dp[q][i]);
                                    //cout<<ans<<' '<<dp[q][i]<<' '<<q<<' '<<i<<endl;
                                    break;
                                }
                            }
                            if(!flag){
                                dp[q][i]=dp[q][i-1]+1;
                                ans=max(ans,dp[q][i]);
                                //cout<<ans<<' '<<dp[q][i]<<' '<<q<<' '<<i<<endl;
                            }
                            break;
                        }
                    }
                }
            }
        }
        //for(ll i=1;i<=max_q;i++)
        //    for(ll j=1;j<=n;j++)
        //        cout<<dp[i][j]<<' '<<i<<' '<<j<<endl;
        printf("%lld
    ",ans);
        return 0;
    }
    View Code

    但其实这还不是正解

    正解非常神仙:

    因为选出的一段是一个等比序列的子序列,我们分为两种情况:
    1. q=1,相当于找一个最长每个数都相等的子串,这个扫一遍就行了。
    2. q!=1,那么这个序列最长只有$log_{2}n$,那么我们可以枚举开头,不妨设开始的两个数为 a[i]和 a[i+1],
    其中比较大的一个为 x,另一个 y。
    (1) 首先要满足 x%y=0
    (2) 让 z=$frac{x}{y}$,然后把 z 质因数分解,z=$p_{1}^{q_2}$×$p_{1}^{q_2}$×$p_{1}^{q_2}$......,设 g=gcd(q1,q2,q3...),那么当前序
    列的最小公比就是 $p_{1}^{frac{q_{1}}{g}}$×$p_{2}^{frac{q_{2}}{h}}$×......
    (3) 我们找到最小公比后,每当往后加一个数,判断它与前边的数的比是否是最小公比的整次幂,
    不是的话就说明不能再加了。
    (4) 还有一个要求就是这个序列里不能有重复的数,这个东西用 set 判断就行了。
    复杂度 O($nlog_{2}^{2}n$);

    正解代码:

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 const long long L=1<<20|1;
      4 char buffer[L],*S,*TT;
      5 #define getchar() ((S==TT&&(TT=(S=buffer)+fread(buffer,1,L,stdin),S==TT))?EOF:*S++)
      6 long long n;
      7 long long data[100001],ans=1,z[100001],q[100001],tot=0;
      8 long long p[100001],c[100001];
      9 set<long long> s;
     10 inline long long read()
     11 {
     12     register long long a=0,b=1;char ch=getchar();
     13     while(ch<'0'||ch>'9')b=(ch=='-')?-1:1,ch=getchar();
     14     while(ch>='0'&&ch<='9')a=(a<<3)+(a<<1)+(ch^48),ch=getchar();
     15     return a*b;
     16 }
     17 inline long long qpow(register long long x,register long long y)
     18 {
     19     register long long ans=1;
     20     while(y)
     21     {
     22         if(y&1)ans*=x;
     23         x*=x;
     24         y>>=1;
     25     }
     26     return ans;
     27 }
     28 inline long long gcd(register long long x,register long long y)
     29 {
     30     register long long i,j;
     31     if(x==0)return y;
     32     if(y==0)return x;
     33     for(i=0;0==(x&1);++i)x>>=1;
     34     for(j=0;0==(y&1);++j)y>>=1;
     35     if(j<i)i=j;
     36     while(1)
     37     {
     38         if(x<y)x^=y,y^=x,x^=y;
     39         if(0==(x-=y))return y<<i;
     40         while(0==(x&1))x>>=1;
     41     }
     42 }
     43 inline void divide(register long long x)
     44 {
     45     tot=0;
     46     for(register long long i=2;i<=min((long long)100,(long long)sqrt(x));i++)
     47         if(!(x%i))
     48         {
     49             p[++tot]=i;c[tot]=0;
     50             while(!(x%i))x/=i,c[tot]++;
     51         }
     52     if(x>1)p[++tot]=x,c[tot]=1;
     53 }
     54 inline bool jud(register long long x,register long long y,register long long q)
     55 {
     56     if(x%y)return 0;
     57     x/=y;
     58     while(x%q==0)x/=q;
     59     if(x!=1)return 0;
     60     return 1;
     61 }
     62 signed main()
     63 {
     64     n=read();
     65     register long long sum=1;
     66     for(register long long i=1;i<=n;++i)q[i]=1;
     67     for(register long long i=1;i<=n;++i)
     68     {
     69         data[i]=read();
     70         if(data[i]==data[i-1])++sum;
     71         else sum=1;
     72         ans=max(ans,sum);
     73         if(i>=2)
     74         {
     75             register long long x=max(data[i],data[i-1]);
     76             register long long y=min(data[i],data[i-1]);
     77             if(x%y)continue;
     78             register long long d=x/y;
     79             divide(d);
     80             register long long g=c[1];
     81                for(register long long m=2;m<=tot;++m)
     82                {
     83                    if(g==1)break;
     84                    g=gcd(g,c[m]);
     85                }
     86             for(register long long l=1;l<=tot;++l)
     87             {
     88                    q[i-1]*=qpow(p[l],c[l]/g);
     89                    if(q[i-1]>10)
     90                    {
     91                        q[i-1]=0;
     92                        break;
     93                 }
     94                }
     95         }
     96     }
     97        for(register long long i=2;i<=n;i++)
     98         if(q[i-1])
     99         {    
    100             if(q[i-1]==1)continue;
    101             s.clear();
    102             s.insert(data[i-1]),s.insert(data[i]);
    103             for(register long long j=i+1;j<=n;j++)
    104             {
    105                 if(s.count(data[j]))break;
    106                 register long long x=max(*--s.end(),data[j]);
    107                 register long long y=min(*--s.end(),data[j]);
    108                 if(jud(x,y,q[i-1]))s.insert(data[j]);
    109                 else break;
    110             }
    111             register long long mm=s.size();
    112                 ans=max(ans,mm);
    113         }
    114     printf("%lld",ans);
    115 }
    非常感谢soul提供的代码,soul有素质有情怀有状态有节操%拜
  • 相关阅读:
    2019春总结作业
    第二周作业
    第三周作业
    2019春第三次课程设计实验报告
    2019春第二次课程设计实验报告
    2019春第一次课程设计实验报告
    第十二周作业
    第十一周作业
    第十周作业
    第九周作业
  • 原文地址:https://www.cnblogs.com/Juve/p/11186800.html
Copyright © 2020-2023  润新知