【bzoj3316】【JC loves MKK】【单调队列+二分答案】

Description

Input

第1行,包含三个整数。n,L,R。
第2行n个数,代表a[1..n]。

Output


仅1行,表示询问答案。
如果答案是整数,就输出整数;否则,输出既约分数“P/Q”来表示。

Sample Input

5 3 4
3 1 2 4 5

Sample Output

7/2

HINT

1≤L≤R≤n≤10^5,0≤ai≤10^9,保证问题有解,数据随机生成

题解:

首先二分答案

每个数减去二分的值,然后就只用判断是否存在一个子串的的和大于0

做一个前缀和,用单调队列维护前缀和递增以及元素个数即可.

因为要求长度必须是偶数所以对奇数位置和偶数位置各维护一个单调队列.

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 200010
#define eps 1e-7
using namespace std;
long double s[N];
int n,L,R,a[N],mx,S,q[2][N],l[2],r[2];
int read(){
  int x(0);char ch=getchar();
  while (ch<'0'||ch>'9') ch=getchar();
  while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
  return x;
}
long long gcd(long long x,long long y){
  if (!y) return x;
  return gcd(y,x%y);
} 
int check(long double x){
  int k;
  for (int i=1;i<=n;i++) s[i]=s[i-1]+a[i]-x;
  l[0]=l[1]=1;r[0]=r[1]=0;
  for (int i=L;i<=n;i++){
    k=i&1;
    while (l[k]<=r[k]&&q[k][l[k]]<i-R) l[k]++;
    while (l[k]<=r[k]&&s[i-L]<s[q[k][r[k]]]) r[k]--;
    q[k][++r[k]]=i-L;
    if (s[i]-s[q[k][l[k]]]>=0) return S=i-q[k][l[k]];
  } 
  return 0;
}
int main(){
  //freopen("a.in","r",stdin);
  n=read();L=read();R=read();
  for (int i=1;i<=n;i++) a[i]=read(),mx=max(mx,a[i]);
  for (int i=1;i<=n;i++) a[i+n]=a[i];
  if (L&1) L++;if (R&1) R--;n=n*2;
  long double l=0,r=mx;
  while(r-l>eps){
    long double mid=(l+r)/2.0;
    if (check(mid)) l=mid;
    else r=mid;
  }
  long double x=(l+r)/2.0;
  long long G=(long long)(S*x+0.5); 
  long long temp=gcd(G,S);
  G=G/temp;S=S/temp;
  if (S==1) cout<<G<<endl;
  else cout<<G<<"/"<<S<<endl;
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值