傳送門
考試題,考場上硬剛剛不出來,沒時間作其餘題了,噁心死我了。
這種很是很差搞的東西通常只有兩種作法嘛:
1.貪心亂搞。
2.固定一維。
枚舉,固然枚舉很差搞的一維y,而後選部分y的木頭改成x
首先咱們>=y的木頭求出能夠構成多少個y,
而後設\(x \in [ky,ky+y)\),咱們能夠選擇>=ky的木頭將其截成x的長度,
咱們貪心地在儘量不干擾y的狀況下選擇,天然選%y最大的了。
咱們下面討論截兩個x的幾種狀況:(爲何是兩個? 廢話,要對y干擾小,還要x儘量大,不/只可能截少許的x嘛)
1.從一根木頭裏截兩根x(有可能出現某一根特別長的木頭)
2.從兩根不一樣的木頭裏截兩根此範圍內的次長的x
3.從兩根不一樣的木頭裏截兩根此範圍內的z最長的x(一根木頭在\([ky,ky+y)\)內,另外一根在\([ky+y,\infty)\)
時間複雜度\(O(\sum_{i=1}^{maxx} maxx/i)=O(maxx log maxx)\)c++
#include<bits/stdc++.h> #define ll long long using namespace std; const int N=5e5+6; int n,t,ky,val,maxx=-1,pre_num,num[N],v[N],pre[N]; ll ans,k,sum=0; inline int read(){ int T=0,F=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();} while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar(); return F*T; } struct Pair{ int mod,w; Pair(int x=-1,int y=-1){mod=x,w=y;} bool operator < (const Pair &a) const {return mod!=a.mod?mod<a.mod:w<a.w;} }; struct xx{ Pair x,y; void update(Pair z){y=max(y,min(x,z)),x=max(x,z);} }; void Max(ll p,ll q){ans=max(ans,(p<2ll?0ll:p*q));} int main(){ n=read(); for(int i=1;i<=n;++i) t=read(),++v[t],maxx=max(maxx,t); for(int i=maxx;i>=0;--i) num[i]=num[i+1]+v[i]; t=0; for(int i=0;i<=maxx;++i){ if(v[i]) t=i; pre[i]=t; } for(int y=2;y<=maxx;++y){ sum=0,ky=maxx+1; xx tmp; for(int i=y;i<=maxx;i+=y) sum+=num[i];//截得出sum根y for(int i=maxx/y;i>=0;--i){ val=pre[ky-1],pre_num=(tmp.x.mod>=0)+(tmp.y.mod>=0),t=i*y; //val:目前範圍中的最大值 if(val>=t){ tmp.update((Pair){val%y,val}); if(v[val]==1) val=pre[val-1]; if(val>=t) tmp.update((Pair){val%y,val}); }//更新最大值,次大值 if(tmp.x.mod>=0) Max(min((ll)(t+tmp.x.mod)>>1,sum-i),y); //從一根木頭裏截 if(tmp.y.mod>=0){ Max(min((ll)t+tmp.y.mod,sum-i-i),y); //兩根木頭裏截次長 if(pre_num+(tmp.x.w<ky)>1) Max(min((ll)t+tmp.x.mod,sum-i-i-1),y); //兩根木頭裏最次長 } ky=t; } } printf("%lld\n",ans); return 0; }