嘘~ 正在从服务器偷取页面 . . .

模拟赛题讲解[29]


模拟赛题讲解[29]

来自 s_r_f 2023-08-03 noi.ac #3197

题目描述

小 C 打算出去旅行。

小 C 的旅行计划中,有 \(n\) 个在考虑中的景点,从 \(1\) 开始标号。

小 C 不想计划的过程太麻烦,所以他参观的景点编号集合一定形如 \(S=\{x|x\in[l,r]\}\),即任意区间 \([l,r]\) 包含的所有编号对应的景点。

每个景点有一个 "快乐值" \(a_i\) 和在该景点花费的时间 \(b_i(b_i > 0)\)

一个旅行计划的快乐程度,定义为参观的景点的快乐值之和 / 时间花费总和。

小 C 打算至少旅行 \(L\) 个景点,至多旅行 \(R\) 个景点。他想知道他的最大快乐程度是多少。

输入格式

第一行,三个整数 \(n\) \(L\) \(R\)

第二行,\(n\) 个整数 \(a_1,\cdots a_n\)

第二行,\(n\) 个整数 \(b_1,\cdots b_n\)

输出格式

输出一行一个实数表示答案。

答案的相对误差或绝对误差不超过 \(10^{-6}\) 会被判为正确答案。

输入样例

5 2 4
100 20 1 30 30
10 1 10 2 5

输出样例

10.9090909091

数据范围

子任务 1(20pts) : \(b_i = 1,L = 1\)

子任务 2(20pts) \(:\) \(n \leq 2000\)

子任务 3(60pts) : \(n \leq 10^5,0 \leq a_i \leq 10^6,1 \leq b_i \leq 10^3, 1\leq L \leq R \leq n\)

题解

显然是 01 分数规划。我们二分这个 happy 值,然后记 \(v_i = a_i - b_ix + v_{i-1}\) (注意是前缀和)

则判断就变成了:是否存在一个区间满足 \(v_r \ge v_l\) 。我们可以固定右端点,维护最小的左端点(单调队列)。

时间复杂度 \(\mathcal{O}\left(n \log \frac{M}{\varepsilon}\right)\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
const double eps = 1e-11;
#define N ((int)(1e5 + 15))

int n,L,R,st,en,q[N]; double a[N];
struct node { int a,b; } c[N];
int dcmp(double x)
{
    if(fabs(x) <= eps) return 0;
    return x > eps ? 1 : -1; 
}
bool check(double x)
{
    for(int i = 1; i <= n; i++) a[i] = a[i - 1] + c[i].a - c[i].b * x;
    q[st = en = 0] = 0;
    for(int i = L; i <= n; i++)
    {
        if(st < en && i - q[st + 1] > R) ++st;
        while(st < en && dcmp(a[q[en]] - a[i - L]) != -1) --en;
        q[++en] = i - L;
        if(dcmp(a[i] - a[q[st + 1]]) != -1) return true;
    }
    return false;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> L >> R; cout << fixed << setprecision(10);
    for(int i = 1; i <= n; i++) cin >> c[i].a;
    for(int i = 1; i <= n; i++) cin >> c[i].b;
    double l = 0, r = 1e12;
    while(abs(r - l) >= 1e-7)
    {
        double mid = (l + r) / 2.0;
        if(check(mid)) l = mid; else r = mid;
    }
    cout << l << '\n';
    return 0;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录