zoukankan      html  css  js  c++  java
  • 2019 Multi-University Training Contest 3

    二分答案 + 平衡树 + dp

    很显然的一道二分答案题,我们每次二分每组最大和,用dp来检测。

    dp[i]表示前i本书能够分成的最多组数。

    可以得到状态转移方程 dp[i] = max(dp[j]) + 1 (sum[i] - sum[j] <= mid) sum为前缀和。

    显然这样是n^2的,考虑优化。

    可以建一颗平衡树,每个节点存前缀和,dp值,已经子树里最大的dp值。

    我们要找之前最大的dp值,又要满足前缀的的限制,将式子转换一下得到 sum[j] >= sum[i] - mid

    我们可以在按sum值维护平衡树,找到sum[i] - mid的后继节点,这样要找的dp值的下标就在该节点以及该节点的右子树里。

    再让每个节点维护它的子树的最大dp值,这样可以迅速的找到答案,只要直接返回该节点的dp值与它的子树的最大dp值中的最大值即可。

    这点在插入节点的时候用pushup就可以实现拉。

    我这里用的是splay,找起来很方便,直接把后继splay到根就好了。

    #include <bits/stdc++.h>
    #define INF 233333333333333333
    #define full(a, b) memset(a, b, sizeof a)
    #define FastIn ios::sync_with_stdio(false), cin.tie(0)
    #define pb push_back
    using namespace std;
    typedef long long LL;
    inline int lowbit(int x){ return x & (-x); }
    inline int read(){
        int ret = 0, w = 0; char ch = 0;
        while(!isdigit(ch)){
            w |= ch == '-', ch = getchar();
        }
        while(isdigit(ch)){
            ret = (ret << 3) + (ret << 1) + (ch ^ 48);
            ch = getchar();
        }
        return w ? -ret : ret;
    }
    inline int lcm(int a, int b){ return a / __gcd(a, b) * b; }
    template <typename A, typename B, typename C>
    inline A fpow(A x, B p, C lyd){
        A ans = 1;
        for(; p; p >>= 1, x = 1LL * x * x % lyd)if(p & 1)ans = 1LL * x * ans % lyd;
        return ans;
    }
    const int N = 300005;
    int _, n, k, a[N], tot, ch[N][2], mxid[N], id[N], fa[N], root, dp[N];
    LL pre[N], lower, upper, val[N];
    
    void go(){
        root = 0, tot = 0;
    }
    
    inline int build(LL v, int i, int f){
        ++ tot;
        val[tot] = v, mxid[tot] = id[tot] = i;
        fa[tot] = f, ch[tot][0] = ch[tot][1] = 0;
        return tot;
    }
    
    inline void push_up(int rt){
        mxid[rt] = id[rt];
        if(ch[rt][0]) mxid[rt] = max(mxid[rt], mxid[ch[rt][0]]);
        if(ch[rt][1]) mxid[rt] = max(mxid[rt], mxid[ch[rt][1]]);
    }
    
    inline void rotate(int x){
        int y = fa[x], z = fa[y], p = (ch[y][1] == x) ^ 1;
        ch[y][p ^ 1] = ch[x][p], fa[ch[x][p]] = y;
        ch[z][ch[z][1] == y] = x, fa[x] = z;
        ch[x][p] = y, fa[y] = x;
        push_up(y), push_up(x);
    }
    
    inline void splay(int x, int goal){
        while(fa[x] != goal){
            int y = fa[x], z = fa[y];
            if(z != fa[y]){
                (ch[y][0] == x) ^ (ch[z][0] == y) ? rotate(x) : rotate(y);
            }
            rotate(x);
        }
        push_up(x);
        if(goal == 0) root = x;
    }
    
    inline void insert(LL x, int i){
        if(!root){
            root = build(x, i, 0);
            return;
        }
        int cur = root;
        while(ch[cur][x > val[cur]]){
            if(val[cur] == x) break;
            cur = ch[cur][x > val[cur]];
        }
        if(val[cur] == x) id[cur] = max(id[cur], i), splay(cur, 0);
        else ch[cur][x > val[cur]] = build(x, i, cur), splay(ch[cur][x > val[cur]], 0);
    }
    
    inline void find(LL x){
        int cur = root;
        while(ch[cur][x > val[cur]] && val[cur] != x)
            cur = ch[cur][x > val[cur]];
        splay(cur, 0);
    }
    
    inline int successor(LL x){
        find(x);
        if(val[root] >= x) return root;
        int cur = ch[root][1];
        while(ch[cur][0]) cur = ch[cur][0];
        return cur;
    }
    
    inline int solve(LL x){
        splay(successor(x), 0);
        return max(id[root], mxid[ch[root][1]]);
    }
    
    inline bool calc(LL mid){
        go();
        insert(INF, 0);
        for(int i = 1; i <= n; i ++){
            int tmp = solve(pre[i] - mid);
            if(tmp == 0 && pre[i] > mid) continue;
            dp[i] = tmp + 1;
            if(dp[i] >= k) return true;
            insert(pre[i], dp[i]);
        }
        return false;
    }
    
    int main(){
    
        //freopen("data.txt", "r", stdin);
        for(_ = read(); _; _ --){
            n = read(), k = read();
            for(int i = 1; i <= n; i ++){
                a[i] = read();
                if(a[i] < 0) lower += a[i];
                else upper += a[i];
                pre[i] = pre[i - 1] + a[i];
            }
            LL l = lower, r = upper;
            while(l < r){
                LL mid = (l + r) >> 1;
                if(calc(mid)) r = mid;
                else l = mid + 1;
            }
            cout << l << endl;
        }
    
        return 0;
    }
    
  • 相关阅读:
    0Day – 2011.1.20[From B4A]
    0Day – 2011.1.16[From B4A]
    ubuntu 桌面下方的面板(任务栏)恢复方法
    Delphi WebBrowser用法几则浅谈
    0Day – 2011.1.3[From B4A]
    0Day – 2011.1.8[From B4A]
    0Day – 2011.1.6[From B4A]
    0Day – 2011.1.10[From B4A]
    0Day – 2011.01.21[From B4A]
    0Day – 2011.1.7[From B4A]
  • 原文地址:https://www.cnblogs.com/onionQAQ/p/11272277.html
Copyright © 2011-2022 走看看