记录编号 130313 评测结果 AAAAAAAAAAAAAAAAAAAA
题目名称 [国家集训队 2012] 几乎平均数 最终得分 100
用户昵称 Gravatarcstdio 是否通过 通过
代码语言 C++ 运行时间 31.414 s
提交时间 2014-10-21 22:43:50 内存使用 982.70 MiB
显示代码纯文本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
typedef long long LL;
const LL INF=(LL)1e12;
const int SIZEN=100010,SIZEK=320;
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
class Frac{
public:
	LL p,q;
};
inline Frac getFrac(LL a,LL b){
	LL g=gcd(a,b);
	a/=g,b/=g;
	if(b<0) a*=-1,b*=-1;
	return (Frac){a,b};
}
void print(Frac a){
	printf("%lld/%lld",a.p,a.q);
}
inline bool operator < (Frac a,Frac b){return (a.p+0.0)*(b.q+0.0)<(b.p+0.0)*(a.q+0.0);}
inline Frac operator + (Frac a,Frac b){return getFrac(a.p*b.q+b.p*a.q,a.q*b.q);}
inline Frac operator - (Frac a,Frac b){return getFrac(a.p*b.q-b.p*a.q,a.q*b.q);}
inline Frac operator * (Frac a,Frac b){return getFrac(a.p*b.p,a.q*b.q);}
class Point{
public:
	double x,y;
	void print(void){cout<<"("<<x<<","<<y<<")";}
};
void print(Point p){cout<<"("<<p.x<<","<<p.y<<")";}
inline Point operator - (Point a,Point b){return (Point){a.x-b.x,a.y-b.y};}
inline double crp(Point a,Point b){
	return a.x*b.y-b.x*a.y;
}
int N,Q,SZ,K;
LL X[SIZEN]={0},S[SIZEN]={0};
int head[SIZEN]={0},tail[SIZEN]={0},belong[SIZEN]={0};
Frac f[SIZEK][SIZEN],g[SIZEK][SIZEN];//f[i][j]:L=head[i],R=j的查询答案;g[i][j]:L=j,R=tail[i]的查询的答案
class Convex_Hull_L_Active{
public:
	//凸包是"上凸"的
	Point H[SIZEN];
	int n;//0~n-1,从右向左
	inline void clear(void){n=0;}
	inline void print(void){for(int i=n-1;i>=0;i--){H[i].print();cout<<" ";}cout<<endl;}
	inline void add(Point p){//p的横坐标小于H中的点
		while(n>=2&&crp(H[n-1]-p,H[n-2]-H[n-1])>=0) n--;
		H[n++]=p;
	}
	inline Frac calc(Point p){//p的横坐标小于H中的点,计算p和凸包上某点形成的最大斜率
		int l=0,r=n-1,mid;
		while(l<r){
			mid=(l+r)/2;
			if(crp(H[mid+1]-p,H[mid]-H[mid+1])>=0) r=mid;
			else l=mid+1;
		}
		return getFrac(H[l].y-p.y,H[l].x-p.x);
	}
};
class Convex_Hull_R_Active{//"右端活跃"的凸包
public:
	//凸包是"下凸"的
	Point H[SIZEN];
	int n;//0~n-1,从左到右
	inline void clear(void){n=0;}
	inline void print(void){for(int i=0;i<n;i++){H[i].print();cout<<" ";}cout<<endl;}
	inline void add(Point p){//p的横坐标大于H中的点
		while(n>=2&&crp(p-H[n-2],H[n-1]-H[n-2])>=0) n--;
		H[n++]=p;
	}
	inline Frac calc(Point p){//p的横坐标大于H中的点,计算p和凸包上某点形成的最大斜率
		int l=0,r=n-1,mid;
		while(l<r){
			mid=(l+r)/2;
			if(crp(p-H[mid],H[mid+1]-H[mid])>=0) r=mid;
			else l=mid+1;
		}
		return getFrac(H[l].y-p.y,H[l].x-p.x);
	}
};
class Super_Hull{//把两种不同姿势的凸包打包成一个类......这是一种诡异的思路
public:
	Convex_Hull_L_Active hull_L;
	Convex_Hull_R_Active hull_R;
	//type=0为左(上凸),type=1为右(下凸)
	void print(bool type){type?hull_R.print():hull_L.print();}
	inline void clear(bool type){type?hull_R.clear():hull_L.clear();}
	inline void add(int k,bool type){
		if(!type) hull_L.add((Point){k,S[k]});
		else hull_R.add((Point){k,S[k-1]});
	}
	inline Frac calc(int k,bool type){
		if(!type) return hull_L.calc((Point){k,S[k-1]});
		else return hull_R.calc((Point){k,S[k]});
	}
}hull;
Frac calc_seg(int l,int r){
	return getFrac(S[r]-S[l-1],r-l);
}
inline Frac calc(int a,int b){
	Frac ans=getFrac(-INF,1);
	if(belong[a]<K&&head[belong[a]+1]<b){
		if(ans<f[belong[a]+1][b]) ans=f[belong[a]+1][b];
	}
	if(belong[b]>1&&tail[belong[b]-1]>a){
		if(ans<g[belong[b]-1][a]) ans=g[belong[b]-1][a];
	}
	/*下面考虑:头在a的块,尾在b的块内的情况,这时我们采用的思路是,
	将右端的元素加入凸包后枚举答案的左端点,因而凸包左活跃,操作代码0*/
	int l_end=min(b-1,tail[belong[a]]),r_end=max(a+1,head[belong[b]]);
	hull.clear(0);
	int k=l_end;
	for(int i=b;i>=r_end;i--){
		hull.add(i,0);
		if(k==i-1){
			Frac tmp=hull.calc(k,0);
			if(ans<tmp) ans=tmp;
			k--;
		}
	}
	for(;k>=a;k--){
		Frac tmp=hull.calc(k,0);
		if(ans<tmp) ans=tmp;
	}
	return ans;
}
void DP_prepare(void){
	//计算f,即向后的情况,这时是向凸包的尾端插入,凸包右活跃,操作代码1
	for(int i=1;i<=K;i++){
		//该块中元素压入凸包
		hull.clear(1);
		for(int j=head[i];j<=tail[i];j++){
			if(j>head[i]) f[i][j]=hull.calc(j,1);
			hull.add(j,1);
		}
		for(int j=tail[i]+1;j<=N;j++) f[i][j]=hull.calc(j,1);
		for(int j=head[i]+1;j<=N;j++) if(f[i][j]<f[i][j-1]) f[i][j]=f[i][j-1];
	}
	for(int i=K-1;i>=1;i--){
		for(int j=head[i+1];j<=N;j++){
			if(f[i][j]<f[i+1][j]) f[i][j]=f[i+1][j];
		}
	}
	//计算g,即向前的情况,这时是向凸包的头部加入,凸包左活跃,操作代码0
	for(int i=K;i>=1;i--){
		//该块中元素压入凸包
		hull.clear(0);
		for(int j=tail[i];j>=head[i];j--){
			if(j<tail[i]) g[i][j]=hull.calc(j,0);
			hull.add(j,0);
		}
		for(int j=head[i]-1;j>=1;j--) g[i][j]=hull.calc(j,0);
		for(int j=tail[i]-1;j>=1;j--) if(g[i][j]<g[i][j+1]) g[i][j]=g[i][j+1];
	}
	for(int i=2;i<=K;i++){
		for(int j=1;j<=tail[i-1];j++){
			if(g[i][j]<g[i-1][j]) g[i][j]=g[i-1][j];
		}
	}
}
void work(void){
	int a,b;
	for(int i=1;i<=Q;i++){
		scanf("%d%d",&a,&b);
		print(calc(a,b));
		printf("\n");
	}
}
void read(void){
	scanf("%d%d",&N,&Q);
	for(int i=1;i<=N;i++) scanf("%lld",&X[i]),S[i]=S[i-1]+X[i];
	SZ=sqrt(N+0.5)*3;
	K=head[1]=1;
	int tot=0;
	for(int i=1;i<=N;i++){
		belong[i]=K;
		tot++;
		if(tot==SZ||i==N){
			tail[K]=i;
			K++;
			tot=0;
			head[K]=i+1;
		}
	}
	K--;
}
int main(){
	freopen("almost.in","r",stdin);
	freopen("almost.out","w",stdout);
	read();
	DP_prepare();
	work();
	return 0;
}