zoukankan      html  css  js  c++  java
  • CF573E Bear and Bowling [平衡树+动态规划]

    Bear and BowlingBear and Bowling

    题意与 这道题目 大致相同, 唯一不同的是所选序列可以在原序列中不连续, 即子序列 .


    color{blue}{最初想法}

    F[i,j]F[i,j] 为第 ii 个数作为选出的 jj 个数中最后一个数时的最优值,
    F[i,1]=A[1],F[0,0]=0F[i,1]=A[1],F[0,0]=0
    F[i,j]=maxk[1,i)(F[k,j1]+A[i]j)F[i,j]=maxlimits_{k∈[1,i)}(F[k,j-1]+A[i]*j)

    时间复杂度 O(N3)O(N^3), 空间复杂度 O(N2)O(N^2), 无法 ACAC,

    F[i,j]F[i,j] 为前 ii 个数, 选出 jj 个数的最优值,
    F[0,0]=0F[0,0]=0

    F[i,j]=max{F[i1,j],F[i1,j1]+jA[i]}F[i,j]=max{F[i-1,j], F[i-1,j-1]+j*A[i]}

    时间复杂度 O(N2)O(N^2), 开滚动数组 空间复杂度 O(N)O(N), 仍无法 ACAC .


    color{red}{正解部分}

    首先要知道一个结论:

    对上面的第二个状态转移方程的每个 F[i,j]F[i, j], 都存在 1Ki1 le K le i,

    • 0j<K0 le j < K 全部由 F[i,j1]F[i,j-1] 转移而来, 在滚动数组中相当于不动 .
    • KjiK le j le i 全部由F[i1,j1]+jA[i]F[i-1,j-1]+j*A[i] 转移而来 .

    KK分界点, 分界点左边称 左区间, 右边 为 右区间 .

    至于证明… , 可以看一下 这位 的博客.


    • 二分找到 分界点,
      具体地说:
      F[mid]<F[mid]+midA[i]F[mid]<F[mid]+mid*A[i],
      说明 midmid 处于右区间, KKmidmid 左边, 于是 r=mid1r = mid - 1,
      否则 l=mid+1l = mid + 1.

    • 找到 分界点 后, 左区间不用管, 只需修改右区间
      列出几个转移
      F[K]=F[K1]+KA[i]F[K+1]=F[K]+(K+1)A[i]F[K+2]=F[K+1]+(K+2)A[i]F[K] = F[K-1]+K*A[i]\ F[K+1]=F[K]+(K+1)*A[i]\ F[K+2]=F[K+1]+(K+2)*A[i]
      发现从 分界点 向右对 滚动数组 加个等差数列即可, 可以使用 线段树 或者 平衡树 实现, 这里用平衡树, 因为标程好拍


    color{red}{实现部分}

    ...color{red}{二分那里有点坑...}

    #include<bits/stdc++.h>
    #define reg register
    typedef long long ll;
    
    const int maxn = 1e5 + 10;
    const ll inf = 1e18;
    
    int N;
    int rot = 1;
    int cnt = 1;
    int fa[maxn];
    int size[maxn];
    int ch[maxn][2];
    
    ll tag_d[maxn];
    ll tag_a[maxn];
    ll V[maxn];
    
    bool chk(int x){ return ch[fa[x]][1] == x; }
    
    void Push_up(int x){ size[x] = size[ch[x][0]] + size[ch[x][1]] + 1; }
    
    void Modify(int x, ll a1, ll d){
            tag_d[x] += d, tag_a[x] += a1;
            V[x] += a1 + d*(size[ch[x][0]] + 1);
    }
    
    void Push_down(int x){
            if(tag_d[x] || tag_a[x]){
                    if(ch[x][0]) Modify(ch[x][0], tag_a[x], tag_d[x]);
                    if(ch[x][1]) Modify(ch[x][1], tag_a[x]+tag_d[x]*(size[ch[x][0]] + 1), tag_d[x]);
                    tag_d[x] = tag_a[x] = 0;
            }
    }
    
    void rotate(int x){ //
            int y = fa[x], z = fa[y], d = chk(x), s = ch[x][d^1];
            ch[y][d] = s, fa[s] = y;
            ch[z][chk(y)] = x, fa[x] = z;
            ch[x][d^1] = y, fa[y] = x;
            Push_up(x), Push_up(y);
    }
    
    void Splay(int x, int aim = 0){ //
            std::stack <int> stk;
            stk.push(x);
            int tmp = x;
            while(fa[tmp]) stk.push(fa[tmp]), tmp = fa[tmp];
            while(!stk.empty()) Push_down(stk.top()), stk.pop();
            while(fa[x] != aim){
                    int y = fa[x], z = fa[y];
                    if(z != aim){
                            if(chk(x) == chk(y)) rotate(y);
                            else rotate(x);
                    }
                    rotate(x);
            }
            rot = x;
    }
    
    ll Kth(int x){
            int t = rot;
            while(1){
                    if(ch[t][0] && size[ch[t][0]] >= x) t = ch[t][0];
                    else if(size[ch[t][0]] + 1 < x) x -= size[ch[t][0]] + 1, t = ch[t][1]; 
                    else{ Splay(t); return V[t]; }
            }
    }
    
    ll Q_max(int x){
            if(!x) return -inf;
            Push_down(x);
            return std::max(V[x], std::max(Q_max(ch[x][0]), Q_max(ch[x][1])));
    }
    
    int Query(int i, int x){ 
            int s = i - 1; 
            int l = 0, r = i - 2;
            while(l <= r){ 
                    ll mid = l+r >> 1; 
                    if(Kth(mid+1) + (mid+1ll)*x > Kth(mid+2)) r = mid - 1, s = mid; 
                    else l = mid + 1; 
            }
            return s;
    }
    
    void Add_node(){
            V[++ cnt] = V[rot];
            fa[cnt] = rot, fa[ch[rot][1]] = cnt;
            ch[cnt][1] = ch[rot][1], ch[rot][1] = cnt;
    }
    
    int main(){ 
    //        freopen("a.in", "r", stdin);
            scanf("%d", &N);
            for(reg int i = 1; i <= N; i ++){
                    int x;
                    scanf("%d", &x);
                    int K = Query(i, x) + 1;
                    Kth(K), Add_node();
                    Modify(cnt, 1ll*(K-1)*x, x);
            }
            printf("%lld
    ", Q_max(rot));
            return 0;
    }
    
    
  • 相关阅读:
    OpenGL(十一) BMP真彩文件的显示和复制操作
    OpenGL(十) 截屏并保存BMP文件
    复数的认识与理解
    复数的认识与理解
    采样定理
    采样定理
    How Many Fibs_hdu_1316(大数).java
    FZOJ2110 star(DFS)
    透过表象看本质!?之二数据拟合
    设计中的道理_3
  • 原文地址:https://www.cnblogs.com/zbr162/p/11822553.html
Copyright © 2011-2022 走看看