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\n",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\n",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\n",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有素质有情怀有状态有节操%拜
posted @ 2019-07-15 07:54  xukl21  阅读(269)  评论(0编辑  收藏  举报