CF573E Bear and Bowling [平衡树+动态规划]

B e a r   a n d   B o w l i n g Bear\ and\ Bowling Bear and Bowling

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


最 初 想 法 \color{blue}{最初想法}

F [ i , j ] F[i,j] F[i,j] 为第 i i i 个数作为选出的 j j j 个数中最后一个数时的最优值,
F [ i , 1 ] = A [ 1 ] , F [ 0 , 0 ] = 0 F[i,1]=A[1],F[0,0]=0 F[i,1]=A[1],F[0,0]=0
F [ i , j ] = max ⁡ k ∈ [ 1 , i ) ( F [ k , j − 1 ] + A [ i ] ∗ j ) F[i,j]=\max\limits_{k∈[1,i)}(F[k,j-1]+A[i]*j) F[i,j]=k[1,i)max(F[k,j1]+A[i]j)

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

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

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

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


正 解 部 分 \color{red}{正解部分}

首先要知道一个结论:

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

  • 0 ≤ j &lt; K 0 \le j &lt; K 0j<K 全部由 F [ i , j − 1 ] F[i,j-1] F[i,j1] 转移而来, 在滚动数组中相当于不动 .
  • K ≤ j ≤ i K \le j \le i Kji 全部由 F [ i − 1 , j − 1 ] + j ∗ A [ i ] F[i-1,j-1]+j*A[i] F[i1,j1]+jA[i] 转移而来 .

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

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


  • 二分找到 分界点,
    具体地说:
    F [ m i d ] &lt; F [ m i d ] + m i d ∗ A [ i ] F[mid]&lt;F[mid]+mid*A[i] F[mid]<F[mid]+midA[i],
    说明 m i d mid mid 处于右区间, K K K m i d mid mid 左边, 于是 r = m i d − 1 r = mid - 1 r=mid1,
    否则 l = m i d + 1 l = mid + 1 l=mid+1.

  • 找到 分界点 后, 左区间不用管, 只需修改右区间
    列出几个转移 ↓ ↓
    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 ] 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] 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]
    发现从 分界点 向右对 滚动数组 加个等差数列即可, 可以使用 线段树 或者 平衡树 实现, 这里用平衡树, 因为标程好拍


实 现 部 分 \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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值