记录编号 |
130313 |
评测结果 |
AAAAAAAAAAAAAAAAAAAA |
题目名称 |
[国家集训队 2012] 几乎平均数 |
最终得分 |
100 |
用户昵称 |
cstdio |
是否通过 |
通过 |
代码语言 |
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;
}