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 }
考完试看正解,没看懂,于是开始更改我的暴力思路
设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; } }
细节的地方包括判断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; }
但其实这还不是正解
正解非常神仙:
因为选出的一段是一个等比序列的子序列,我们分为两种情况:
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 }