Wooden_Raft题解

Wooden_Raft题解

传送门
考试题,考场上硬刚刚不出来,没时间做其他题了,恶心死我了
这种非常不好搞的东西一般只有两种做法嘛:
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)\)

#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;
} 
posted @ 2019-10-26 07:54  lsoi_ljk123  阅读(169)  评论(0编辑  收藏  举报