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]=\max\limits_{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\n", Q_max(rot));
        return 0;
}

posted @ 2019-07-17 20:52  XXX_Zbr  阅读(233)  评论(0编辑  收藏  举报