2019杭电多校第三场 1004 Distribution of books

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6606

考虑二分答案,我们二分一个值\(x\),那么要怎么来验证这个答案是否可行,考虑dp求解,设\(dp[i]\)为前i个在答案为\(x\)的情况下划分最最多组数,那么若\(dp[n] \geq k\) 则这个x可行, 很显然可以看出\(x\)是单调的,所以二分。
\[dp[i] = max(dp[j]) + 1 (sum[i] - sum[j-1] \leq x)\]
如果直接采用暴力枚举的话复杂度\(O(n^2)\),不妨考虑离散化之后建权值线段树。我们将所有的\(sum[i]\)离散化给定标号后, 我们从\(i=1\)开始遍历到\(n\)的过程中,对于每个i,只需要找到第一个\(sum[t] \geq sum[i] - x\)也就是\(sum[i]-x\)的后继。 然后在权值线段树上取query求\([t, m]\)的最大值即可,\(m\)为权值线段树的节点个数。 时间复杂度\(O(nlog^2n)\)

#include <bits/stdc++.h>
#define pii pair<ll, ll>
#define pil pair<ll, long long>
#define pll pair<long long, long long>
#define lowbit(x) ((x)&(-x))
#define mem(i, a) memset(i, a, sizeof(i))
#define sqr(x) ((x)*(x))
#define all(x) x.begin(),x.end()
#define ls (k << 1)
#define rs (k << 1 | 1)
using namespace std;
typedef long long ll;
template <typename T>
inline void read(T &X) {
    X = 0; char ch = 0; T op = 1;
    for(; ch > '9' || ch < '0'; ch = getchar())
        if(ch == '-') op = -1;
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        X = (X << 3) + (X << 1) + ch - 48;
    X *= op;
}

const ll INF = 0x3f3f3f3f;
const ll N = 2e5 + 5;
vector<ll> v;
struct node {
    ll l,r,w = 0;
}tr[N * 4];
ll a[N],sum[N];
ll n,k,m;
void build(ll k, ll l, ll r) {
    tr[k].l = l; tr[k].r = r;
    if(l == r) {
        tr[k].w = 0; return;
    }
    ll mid = l + r >> 1;
    build(ls, l, mid);
    build(rs, mid + 1, r);
    tr[k].w = 0;
}
void update(ll k, ll p, ll x) {
    ll l = tr[k].l, r = tr[k].r;
    ll mid = l + r >> 1;
    if(l == r) {tr[k].w = max(tr[k].w, x); return;}
    if(p <= mid) update(ls, p, x);
    else update(rs, p, x);
    tr[k].w = max(tr[ls].w, tr[rs].w);
}
ll query(ll k, ll s, ll t) {
    ll l = tr[k].l, r = tr[k].r;
    if(s <= l && t >= r) {
        return tr[k].w;
    }
    ll ans = 0;
    ll mid = l + r >> 1;
    if(s <= mid) ans = max(ans, query(ls, s, t));
    if(t > mid) ans = max(ans, query(rs, s, t));
    return ans;
}
bool check(ll x) {
    build(1, 1, m);
    for(ll i = 1; i <= n; i++) {
        ll l = lower_bound(all(v), sum[i] - x) - v.begin() + 1;
        ll r = lower_bound(all(v), sum[i]) - v.begin() + 1;
        //cout << l << " " << r << " " << i << "\n";
        if(l > m) {
            if(sum[i] <= x) update(1, r, 1);
            continue;
        }
        ll w = query(1, l, m);
        if(w) {
            update(1, r, w + 1);
        } else if(sum[i] <= x) {
            update(1, r, 1);
        }
    }
  // cout << query(1, 1, m) << " " << x << "\n";
    return query(1, 1, m) >= k;
}
int main() {
#ifdef INCTRY
    freopen("input.txt", "rt", stdin);
#endif
    ll t;
    cin >> t;
    while(t--) {
        read(n);  read(k);
        ll mi = -INF, mx = INF;
        v.clear();
        for(ll i = 1; i <= n; i++) {
            read(a[i]); sum[i] = sum[i - 1] + a[i];
            v.push_back(sum[i]);
        }

        sort(all(v)); v.erase(unique(all(v)), v.end());
        m = v.size();
        /* for(ll i = 1; i <= n; i++) {
            id[i] = lower_bound(all(v), sum[i]) - v.begin() + 1;
        }*/
        ll l = -1e14-10, r = 1e14+10;
        ll ans = 0;
         while(l <= r) {
            ll mid = l + r >> 1;
            //cout << mid << "\n";
            if(check(mid)) ans = mid, r = mid - 1;
            else l = mid + 1;
        }

        //cout << query(1, 1, m) << " " << k << "\n";
        cout << ans << "\n";
    }

#ifdef INCTRY
    cerr << "\nTime elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << " s.\n";
#endif
    return 0;
}

原文地址:https://www.cnblogs.com/inctry/p/11267516.html

时间: 2024-09-29 10:00:33

2019杭电多校第三场 1004 Distribution of books的相关文章

2019 杭电多校 第三场

2019 Multi-University Training Contest 3 补题链接:2019 Multi-University Training Contest 3 1002 Blow up the city (HDU-6604) 题意 给定 \(n\) 个点和 \(m\) 条边的有向无环图,给出 \(q\) 次询问,每个询问给出 \(a\) 和 \(b\),求有多少个点,满足该点删去后 \(a\) 和 \(b\) 中至少一个点不能到达出度为 \(0\) 的点. 题解 支配树/灭绝树 拓

2019杭电多校第三场 1008 K-th Closest Distance

题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=6621 考虑主席树,我们先将所有值离散化之后建主席树.对于每个查询\(s,t,p,k\) 我们考虑二分一个值\(mid\),考虑当前区间内,\([p-mid, p+mid]\)的值有多少个,很显然这是符合单调性的,那么我们只需要每次判断即可.时间复杂度\(O(nlog^2n)\) #include <bits/stdc++.h> #define pii pair<int, int> #d

2019 杭电多校 第五场

2019 Multi-University Training Contest 5 补题链接:2019 Multi-University Training Contest 5 罚时爆炸 自闭场 1004 equation (HDU 6627) 题意: 给定一个整数 \(C\) 和 \(N\) 组 \(a_i,b_i\),求 \(∑_{i=1}^N|a_i\cdot x + b_i| = C\) 的所有解,如果有无穷多个解就输出 -1. 思路 分类讨论 分类讨论去绝对值.根据 \(b_i / a_i

2019年杭电多校第三场 1011题Squrirrel(HDU6613+树DP)

题目链接 传送门 题意 给你一棵无根树,要你寻找一个根节点使得在将一条边权变为\(0\)后,离树根最远的点到根节点的距离最小. 思路 本题和求树的直径很像,不过要记得的东西有点多,且状态也很多. \(fi[u][0]\)表示在\(u\)这个结点不删边沿着子树方向能到达的最远距离,\(se[u][0]\)为第二远,\(th[u][0]\)为第三远,\(fa[u][0]\)表示沿着父亲方向能到达的最远距离,第二维为\(1\)表示删一条边能到达的距离. 不删边的转移和求树的直径转移方程基本上是一样的,

2018 Multi-University Training Contest 3 杭电多校第三场

躺了几天 终于记得来填坑了 1001 Ascending Rating   (hdoj 6319) 链接:http://acm.hdu.edu.cn/showproblem.php?pid=6319 单调队列 具体有点类似双端队列滑动窗口 题意:在一个队列中 每次都给定一个固定长度的区间 从i=1开始向后移动 每次在这个区间中进行a[i]和a[j]的比较 若a[i]<a[j] count++ 最大值更新为a[j] ,每个区间的最大值和count都分别异或i 求出分别的和 求区间最大值可以比较容易

2019 杭电多校 第八场

2019 Multi-University Training Contest 8 补题链接:2019 Multi-University Training Contest 8 1003 Acesrc and Good Numbers HDU 6659 题意 定义 \(f(d, n)\) 为十进制下 \(1\) 到 \(n\) 所有数的数位中数字 \(d\) 出现的次数.给定 \(x\),找出最大的 \(n(n \le x)\) 满足 \(f(d, n) = n\). 题解 看到了一个神仙做法. 显

2019 杭电多校 第七场

2019 Multi-University Training Contest 7 补题链接:2019 Multi-University Training Contest 7 1001 A + B = C 题意: 给出 \(a, b, c\),求 \(x, y, z\) 满足 \(a\cdot 10^x + b\cdot 10^y = c\cdot 10^z\).\(a, b, c \le 10^{100000}\). 题解: 补零到 \(a, b, c\) 长度相等之后,可能的情况只有四种: \

2019 杭电多校 第六场

2019 Multi-University Training Contest 6 补题链接:2019 Multi-University Training Contest 6 1002 Nonsense Time (HDU 6635) 题意 给定包含 \(n\) 个不同数字的排列 \(p\).一开始所有数字都冻住.再给出一个长度为 \(n\) 的数组 \(k\),\(k[i]\) 表示 \(p[k[i]]\) 在第 \(i\) 时刻解冻.输出 \(n\) 个数,表示第 \(i\) 个时刻数组 \(

2019 杭电多校 第四场

2019 Multi-University Training Contest 4 补题链接:2019 Multi-University Training Contest 4 1001 AND Minimum Spanning Tree (HDU 6614) 题意 给定一个有 \(N\) 个结点的完全图,编号从 \(1\) 到 \(N\).结点 \(x\) 与结点 \(y\) \((1\leq x, y\leq N, x \neq y)\) 的边的权值为 \(x\) 与 \(y\) 按位与的值,求