2670: Almost|分块|三分
Posted ws_yzy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2670: Almost|分块|三分相关的知识,希望对你有一定的参考价值。
先处理出前缀和
sumi
区间
[l..r]
的几乎平均数为
也就是求一个斜率的最大值,假如左端点确定,找一个右端点使得几乎平均数最大的话,可以求出凸包,然后再凸包上3分找到最大值
然后就可以分块设立 T 个关键点求出每个点到关键点这个区间的最大的几乎平均数
询问的时候可以先拽出跨过关键点的答案,零碎的部分暴力
然而我的代码自带大常数只有 80 分
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<ctime>
#include<set>
#include<map>
#define N 100005
#define LL long long
using namespace std;
int sc()
int i=0,f=1; char c=getchar();
while(c>'9'||c<'0')if(c=='-')f=-1;c=getchar();
while(c>='0'&&c<='9')i=i*10+c-'0',c=getchar();
return i*f;
struct Wint l,r;f[310][N];
long long sum[N];
int n,m,block,id[333],bl[N],q[N];
double slop(int x,int y)
return (double)(sum[x]-sum[y])/(x-y);
double slop1(int x,int y)
return (double)(sum[x-1]-sum[y-1])/(x-y);
double cal(int l,int r)
return (double)(sum[r]-sum[l-1])/(r-l);
long long gcd(LL x,LL y)
return x==0?y:gcd(y%x,x);
void print(int l,int r)
long long x=sum[r]-sum[l-1],y=r-l,p=gcd(abs(x),abs(y));
printf("%lld/%lld\\n",x/p,y/p);
void solve(int l,int r)
double mx=-11111111111.0;
int ql=n,qr=n-1,lx,rx;
for(int i=r;i>l;i--)
while(ql<qr&&slop(i,q[ql])<slop(i,q[ql+1]))ql++;
q[--ql]=i;
int L=ql,R=qr;
while(R-L>5)
int Lmid=L+(R-L)/3;
int Rmid=R-(R-L)/3;
if(cal(i-1,q[Lmid])>cal(i-1,q[Rmid]))
R=Rmid;
else L=Lmid;
for(int j=L;j<=R;j++)
double now=cal(i-1,q[j]);
if(now>mx) mx=now,lx=i-1,rx=q[j];
print(lx,rx);
void pre()
for(int i=1;i<=bl[n];i++)
int x=id[i],ql=n,qr=n-1;
double ans=-111111111111.0;
for(int j=x-1;j>=1;j--)
f[i][j]=f[i][j+1];
while(ql<qr&&slop(j+1,q[ql])<slop(j+1,q[ql+1]))ql++;
q[--ql]=j+1;
int L=ql,R=qr;
while(R-L>5)
int Lmid=L+(R-L)/3;
int Rmid=R-(R-L)/3;
if(cal(j,q[Lmid])>cal(j,q[Rmid]))
R=Rmid;
else L=Lmid;
for(int k=L;k<=R;k++)
double now=cal(j,q[k]);
if(now>ans)
ans=now;
f[i][j].l=j;
f[i][j].r=q[k];
ql=1,qr=0; ans=-111111111111.0;
for(int j=x+1;j<=n;j++)
f[i][j]=f[i][j-1];
while(ql<qr&&slop1(q[qr],j-1)<slop1(q[qr-1],j-1))qr--;
q[++qr]=j-1;
int L=ql,R=qr;
while(R-L>5)
int Lmid=L+(R-L)/3;
int Rmid=R-(R-L)/3;
if(cal(q[Lmid],j)>cal(q[Rmid],j))
R=Rmid;
else L=Lmid;
for(int k=L;k<=R;k++)
double now=cal(q[k],j);
if(now>ans)
ans=now;
f[i][j].l=q[k];
f[i][j].r=j;
int main()
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
sum[i]=sum[i-1]+sc();
block=n/sqrt(m);
for(int i=1;i<=n;i++)
bl[i]=(i-1)/block+1;
if(bl[i]!=bl[i-1]) id[bl[i]]=i;
pre();
while(m--)
int l=sc(),r=sc();
if(bl[r]-bl[l]<=1) solve(l,r);
else
int ll=bl[l]+1,rr=bl[r];
int lx,rx;
double a1=cal(f[ll][r].l,f[ll][r].r);
double a2=cal(f[rr][l].l,f[rr][l].r);
double ans;
if(a1>a2)
lx=f[ll][r].l;
rx=f[ll][r].r;
ans=a1;
else
lx=f[rr][l].l;
rx=f[rr][l].r;
ans=a2;
int ql=n,qr=n-1;
for(int i=r;bl[i]==rr;i--)
while(ql<qr&&slop(i,q[ql])<slop(i,q[ql+1]))ql++;
q[--ql]=i;
for(int i=l;bl[i]<ll;i++)
int L=ql,R=qr;
while(R-L>5)
int Lmid=L+(R-L)/3;
int Rmid=R-(R-L)/3;
if(cal(i,q[Lmid])>cal(i,q[Rmid]))
R=Rmid;
else L=Lmid;
for(int k=L;k<=R;k++)
double now=cal(i,q[k]);
if(now>ans) ans=now,lx=i,rx=q[k];
print(lx,rx);
return 0;
以上是关于2670: Almost|分块|三分的主要内容,如果未能解决你的问题,请参考以下文章