• [2018冬令营模拟测试赛(二十一)]Problem A: Decalcomania


    [2018冬令营模拟测试赛(二十一)]Problem A: Decalcomania

    试题描述

    QAQ

    TAT

    输入

    见“试题描述

    输出

    见“试题描述

    输入示例

    见“试题描述

    输出示例

    见“试题描述

    数据规模及约定

    见“试题描述

    题解

    最优解一定是在一段连续的包含 (0) 号点的区间中选择最小的 (k) 个印花,其中一边需要折返(即代价为两倍距离)。

    所以我们先将一边的距离 ( imes 2),然后两边分别求出 (f_k) 表示印 (k) 个花至少需要花费的代价,最后枚举一边的答案,并通过剩余步数二分得到另一边的答案即可。

    考虑一边的 (f_k) 如何处理,这个方案其实就是确定一个点 (u),然后印 (0)(u) 之间的最小的 (k) 个花,不难发现随着 (k) 增加,(u) 也是单调移动的。由于不好确定最优解具体在哪,我们考虑分治解决这个问题,令 (solve(l, r, ql, qr)) 表示答案在区间 ([l, r]) 中,询问区间为 ([ql, qr]),这个时候我们询问一下 ([ql, qr]) 的中点(这个询问就是暴力扫一遍 ([l, r]) 然后再数据结构查询一下前 (k) 小的总和,找到最优的位置 (p),下次左半部分的询问归到 ([l, p]) 中,右半部分归到 ([p, r]) 中),然后继续递归下去就好了。我们需要一个数据结构支持查询区间内前 (k) 小的数之和,主席树就可以实现。

    最后总复杂度是 (O(n log^2 n)),那个分治是 (O(n log n)) 的,因为每层最多询问 (O(n)) 次,有 (log n) 层。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    #define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
    #define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
    #define LL long long
     
    LL read() {
        LL x = 0, f = 1; char c = getchar();
        while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
        while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
        return x * f;
    }
     
    #define maxn 100010
    #define maxnode 2000010
    #define ool (1ll << 60)
    #define pli pair <LL, int>
    #define x first
    #define y second
    #define mp(x, y) make_pair(x, y)
     
    int n, dis[maxn], Time[maxn], num[maxn];
    LL lim;
     
    pli Info[maxn];
    LL sumv[maxnode];
    int ToT, rt[maxn], lc[maxnode], rc[maxnode], siz[maxnode];
    void update(int& y, int x, int l, int r, int p) {
        sumv[y = ++ToT] = sumv[x] + num[p];
        siz[y] = siz[x] + 1;
        if(l == r) return ;
        int mid = l + r >> 1; lc[y] = lc[x]; rc[y] = rc[x];
        if(p <= mid) update(lc[y], lc[x], l, mid, p);
        else update(rc[y], rc[x], mid + 1, r, p);
        return ;
    }
    LL query(int o, int l, int r, int k) {
        if(!o) return 0;
        if(siz[o] == k) return sumv[o];
        int mid = l + r >> 1;
        if(k <= siz[lc[o]]) return query(lc[o], l, mid, k);
        else return sumv[lc[o]] + query(rc[o], mid + 1, r, k - siz[lc[o]]);
    }
    LL Query(int p, int k) {
        if(k > p) return ool;
        return query(rt[p], 1, n, k) + Info[p].x;
    }
    void process(int l, int r, int ql, int qr, LL *ans) {
        int mid = ql + qr >> 1;
        LL res = ool, opt;
        rep(i, l, r) {
            LL tmp = Query(i, mid);
            if(res > tmp) res = tmp, opt = i;
        }
        ans[mid] = res;
        if(ql == qr) return ;
        process(l, opt, ql, mid, ans);
        process(opt, r, mid + 1, qr, ans);
        return ;
    }
    void solve(LL *ans, int N) {
        ToT = 0; memset(lc, 0, sizeof(lc)); memset(rc, 0, sizeof(rc));
        rep(i, 1, N) update(rt[i], rt[i-1], 1, n, Info[i].y);
        process(1, N, 1, N, ans);
        return ;
    }
     
    LL ans1[maxn], ans2[maxn];
    int main() {
        n = read(); lim = read();
        rep(i, 1, n) dis[i] = read(), num[i] = Time[i] = read();
         
        sort(num + 1, num + n + 1);
        rep(i, 1, n) Time[i] = lower_bound(num + 1, num + n + 1, Time[i]) - num;
         
        int ans = 0;
         
        Info[1] = mp(0, Time[1]);
        rep(i, 2, n) Info[i] = mp(Info[i-1].x + (dis[i-1] << 1), Time[i]);
        solve(ans1, n);
        Info[1] = mp(dis[n], Time[n]);
        rep(i, 2, n - 1) Info[i] = mp(Info[i-1].x + dis[n-i+1], Time[n-i+1]);
        solve(ans2, n - 1);
        rep(i, 1, n) if(ans1[i] <= lim) ans = max(ans, (int)(i + upper_bound(ans2 + 1, ans2 + n, lim - ans1[i]) - ans2 - 1));
        rep(i, 1, n - 1) if(ans2[i] <= lim) ans = max(ans, i);
         
        Info[1] = mp(0, Time[1]);
        rep(i, 2, n) Info[i] = mp(Info[i-1].x + dis[i-1], Time[i]);
        solve(ans1, n);
        Info[1] = mp(dis[n] << 1, Time[n]);
        rep(i, 2, n - 1) Info[i] = mp(Info[i-1].x + (dis[n-i+1] << 1), Time[n-i+1]);
        solve(ans2, n - 1);
        rep(i, 1, n) if(ans1[i] <= lim) ans = max(ans, (int)(i + upper_bound(ans2 + 1, ans2 + n, lim - ans1[i]) - ans2 - 1));
        rep(i, 1, n - 1) if(ans2[i] <= lim) ans = max(ans, i);
         
        printf("%d
    ", ans);
         
        return 0;
    }
    
  • 相关阅读:
    自定义Spark Partitioner提升es-hadoop Bulk效率
    golang channel本质——共享内存
    STM 软件事务内存——本质是为提高并发,通过事务来管理内存的读写访问以避免锁的使用
    elasticsearch 自定义_id
    JS弄ASP.NET(C#)在页GridView信息选择行
    pdf转换为word小工具,挺好
    Cocos2d-x场景功能描述的生命周期
    数据收集程序一般建筑(C++ ACE达到)
    IOS上传文件开发
    thinkphp达到UploadFile.class.php图片上传功能
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/8486633.html
Copyright © 2020-2023  润新知