首页 > 代码库 > bzoj2006 [NOI2010]超级钢琴

bzoj2006 [NOI2010]超级钢琴

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2006

【题解】

思路巧妙啊!

前置技能:序列和可以转化成前缀和的形式,那么前缀和左端点固定了右端点就是区间找最大值了。

记录五元组(from, l, r, pos, val)表示从from开始,右端点在[l,r]之间,在pos处取max,取max的值是val。

那么按照val扔到堆里,每次取出最大的,从pos分裂成两半,要兹磁求最大值,所以st表就行了。

技术分享
# include <queue>
# include <stdio.h>
# include <string.h>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 5e5 + 10;
const int mod = 1e9+7;

# define RG register

struct pa {
    int x, l, r, mx, v; 
    pa() {}
    pa(int x, int l, int r, int mx, int v) : x(x), l(l), r(r), mx(mx), v(v) {}
    friend bool operator < (pa a, pa b) {
        return a.v<b.v;
    }    
};

int n, k, L, R;
int a[M], s[M];

namespace ST {
    struct rmq {
        int i, v;
        rmq() {}
        rmq(int i, int v) : i(i), v(v) {}
        friend bool operator <(rmq a, rmq b) {
            return a.v<b.v;
        }
        friend bool operator >(rmq a, rmq b) {
            return a.v>b.v;
        }
    };
    int n, Log2[M];
    rmq f[M][20];
    inline void init(int _n) {
        n = _n;
        Log2[1] = 0;
        for (int i=2; i<=n; ++i) Log2[i] = Log2[i>>1] + 1;
        for (int i=1; i<=n; ++i) f[i][0] = rmq(i, s[i]);
        for (int j=1; j<=19; ++j)
            for (int i=1; i+(1<<j)-1<=n; ++i) 
                 f[i][j] = max(f[i][j-1], f[i+(1<<(j-1))][j-1]);
    }
    inline bool getpos(int l, int r, int &i, int &v) {
        if(l > r) return 0;
        int len = Log2[r-l+1];
        rmq t = max(f[l][len], f[r-(1<<len)+1][len]);
        i = t.i, v = t.v;
        return 1;
    }
}

priority_queue<pa> q;

int main() {
    scanf("%d%d%d%d", &n, &k, &L, &R);
    for (int i=1; i<=n; ++i) {
        scanf("%d", a+i); 
        s[i] = s[i-1] + a[i];
    }
    ST::init(n);
    for (int i=1; i<=n; ++i) {
        if(i+L-1>n) break;
        int l = i+L-1, r = min(i+R-1, n);
        int pos, va;
        ST::getpos(l, r, pos, va);
        q.push(pa(i, l, r, pos, va-s[i-1]));
    }
    
    ll ans = 0;
    for (int i=1; i<=k; ++i) {
        pa t = q.top();
        ans = ans + t.v;
        q.pop();
        
        int pos, val;
        if(ST::getpos(t.l, t.mx-1, pos, val)) 
            q.push(pa(t.x, t.l, t.mx-1, pos, val-s[t.x-1]));
        if(ST::getpos(t.mx+1, t.r, pos, val)) 
            q.push(pa(t.x, t.mx+1, t.r, pos, val-s[t.x-1]));            
    } 
    
    printf("%lld\n", ans);

    return 0;
}
View Code

 

bzoj2006 [NOI2010]超级钢琴