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
考完试看正解,没看懂,于是开始更改我的暴力思路
设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,那么这个序列最长只有 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
以上是关于HZOI2019序列的主要内容,如果未能解决你的问题,请参考以下文章
[HZOI 2016][Tyvj 1729]文艺平衡树 这道题我真是哭了,调了一下午,一晚上