HZOI2019序列

Posted juve

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HZOI2019序列相关的知识,希望对你有一定的参考价值。

题目链接:https://www.cnblogs.com/Juve/articles/11186805.html

题解:

这题我考试打的暴力,只有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,那么这个序列最长只有 logn,那么我们可以枚举开头,不妨设开始的两个数为 a[i]和 a[i+1],
其中比较大的一个为 x,另一个 y。
(1) 首先要满足 x%y=0
(2) 让 z=x/y,然后把 z 质因数分解,z=p1^q1*p2^q2*p3^q3......,设 g=gcd(q1,q2,q3...),那么当前序
列的最小公比就是 p1^(q1/g)*p2^(q2/h)*......
(3) 我们找到最小公比后,每当往后加一个数,判断它与前边的数的比是否是最小公比的整次幂,
不是的话就说明不能再加了。
(4) 还有一个要求就是这个序列里不能有重复的数,这个东西用 set 判断就行了。
复杂度 nlog22n;

正解代码:

技术图片
  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有素质有情怀有状态有节操%拜

以上是关于HZOI2019序列的主要内容,如果未能解决你的问题,请参考以下文章

COGS2287 [HZOI 2015]疯狂的机器人

cogs 2632. [HZOI 2016] 数列操作d

[HZOI 2016][Tyvj 1729]文艺平衡树 这道题我真是哭了,调了一下午,一晚上

COGS 2479. [HZOI 2016]偏序 [CDQ分治套CDQ分治 四维偏序]

cogs 2320. [HZOI 2015]聪聪的世界题解

数据结构(主席树):HZOI 2016 采花