传送门
这道题其实乍一看很难,但其实就是一道推数学规律的题,只要把这个规律推出来了,剩下的也就很好办了。
最关键的就是题面中所给的那个式子,bi+bj+gcd(bi,bj)=lcm(bi,bj),我们令 x=gcd(bi,bj),pi=bi÷x,pj=bj÷x,则根据最小公因数和最大公倍数的定义可以得到:
pi⋅x+pj⋅x=x⋅pi⋅pj−xpi+pj=pi⋅pj−1pi⋅pj−pi−pj+1=2(pi−1)⋅(pj−1)=2
很明显可以知道
pi∈N+,pj∈N+
于是就可以推出
pi=2,pj=3
所以
bi:bj=2:3
也就是说,对于一个数,要么它是序列中最大的那个,要么就有另外一个数是它的 23 倍。
因此,对于选出来的序列 b1,b2,b3,⋯,bk,将其去重并从小到大排序后,应该满足 23bi=bi+1,也就是一个公比为 23 的等比数列,至于为什么是去重后,因为我们只关心这个数有没有比它大 21 的数,只要满足这个条件,无论有几个和这个数相同的数,我们都可以把它们放在序列里面,比如 2,2,2,3,3 这个序列就是满足条件的。
这样,问题就转化成了,在给定序列中寻找一个公比为 23 的等比序列,并且这个序列中所有数的和最大。对于这个问题,我的思路是 DP,先对原序列进行排序和去重,用 fi 表示以第 i 个数结尾的公比为 23 的等比数列中所有数的最大的和,状态转移方程也很好想,就是对于 ai,找有没有 aj=32ai,如果有,那么 fi←fj+fi,如果没有,就不管它,由于事先已经对序列排序并去重,所以这个找的过程可以用二分解决(别跟我说桶排,109 的值域你自己去排),时间复杂度为 O(nlogn),对付 3×105 的数据也够了。
至于预处理,在去重的时候顺便解决一下就好了,具体的可以看代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| #include<bits/stdc++.h> using namespace std; int n,k,a[300001],b[300001]; long long ans,f[300001]; int check(int l,int r,int val) { while(l<=r) { int mid=(l+r)/2; if(b[mid]==val) return mid; if(b[mid]>val) r=mid-1; if(b[mid]<val) l=mid+1; } return -1; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+1+n); for(int i=1;i<=n;i++) { if(a[i]!=a[i-1]) b[++k]=a[i]; f[k]+=a[i]; } for(int i=1;i<=k;i++) { if(b[i]%3==0) { int t=check(1,i,b[i]/3*2); if(t!=-1) f[i]+=f[t]; } ans=max(ans,f[i]); } printf("%lld",ans); return 0; }
|