NOI2010 超级钢琴 ST表+堆+贪心

(题目见这里)

题目大意:求最大的k个长度在[L,R]之间的子段和。
刚学了ST表,拿这道题拓展视野。
首先看到求子段和,很容易想到用前缀和,答案又限定在一段区间内,可以暴力求出所有长度符合条件的子段,再判断大小。显然不可过,于是想到用贪心。(其实是在标签上看到的)

用一个三元组(o,l,r)表示以o为起点,最优解在[l,r]之间的答案。先预处理每个起点,而后询问k个最优解。每次询问过后,由于最优解可能还存在于这一区间的其他端点上,所以要把这个区间分为2段(o,l,mid)和(o,mid+1,r)插入到优先队列中。

#include
#include
#include
using namespace std;
const int MAXN = 500001;

int sum[MAXN];

inline int read(){
    int k = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9'){k = k*10 + ch - '0'; ch = getchar();}
    return k * f;
}

class Sparse_Table{
    private:
        int Log[MAXN], Pow[21];
        int f[MAXN][21];
        inline int Max(int a, int b){
            return sum[a] > sum[b] ? a : b;
        }
    public:
        inline void init(int n){
            Pow[0] = 1, Log[0] = -1;
            for(int i = 1; (1 << i) <= n; i++) Pow[i] = Pow[i - 1] << 1;
            for(int i = 1; i <= n; i++) Log[i] = Log[i >> 1] + 1;
            for(int i = 1; i <= n; i++) f[i][0] = i;
            for(int k = 1; (1 << k) <= n; k++)
            for(int i = 1; i + Pow[k] - 1 <= n; i++)
                f[i][k] = Max(f[i][k - 1], f[i + Pow[k - 1]][k - 1]);
        }
        inline int query(int l, int r){
            int t = Log[r - l + 1], R = r - Pow[t] + 1;
            return Max(f[l][t], f[R][t]);
        }
};
Sparse_Table ST;

struct node{
    int l, r, o, t;
    node(int o, int l, int r) : o(o), l(l), r(r), t(ST.query(l, r)) {}
    bool operator < (const node & x) const {
        return sum[t] - sum[o - 1] < sum[x.t] - sum[x.o - 1];//注意:存入优先队列的是最大子段和,不是前缀和
    }
};
priority_queue <node> q; //用优先队列代替堆(其实是我堆写错了)

/*struct node{
    int l, r, o, t;
    node() {}
    node(int o, int l, int r) : o(o), l(l), r(r), t(ST.query(l, r)) {}
};

class HEAP{
    private:
        node h[MAXN << 2];
        int tail;
        inline bool cmp(int a, int b){
            return sum[h[a].t] - sum[h[a].o - 1] > sum[h[b].t] - sum[h[b].o - 1];
        }
    public:
        inline node top(){return h[1];}
        inline void push(node x){
            int u, fa;
            h[++tail] = x;
            u = tail;
            while(u > 1){
//				printf("u = %d, tail = %d\n", u, tail);
                fa = u >> 1;
                if(cmp(fa, u)) break;
                swap(h[u], h[fa]);
                u = fa;
            }
        }
        inline void pop(){
            swap(h[1], h[tail--]);
            int u = 1, nxt;
            while((u << 1) <= tail){
                nxt = u << 1;
                if((nxt | 1) <= tail && cmp(nxt, nxt | 1)) nxt |= 1;
                if(cmp(u, nxt)) break;
                swap(h[u], h[nxt]);
                u = nxt;
            }
        }
};
HEAP q;*/


int main(){
    int n = read(), k = read(), L = read(), R = read();
    for(int i = 1; i <= n; i++)
        sum[i] = sum[i - 1] + read();
    ST.init(n);
    for(int i = 1; i + L - 1 <= n; i++){
        q.push((node){i, i + L - 1, min(i + R - 1, n)});
    }
    long long ans = 0;
    for(int i = 1; i <= k; i++){
        int u = q.top().o, l = q.top().l, r = q.top().r, t = q.top().t; q.pop();
        ans += sum[t] - sum[u - 1];
        if(l != t) q.push((node){u, l, t - 1});
        if(r != t) q.push((node){u, t + 1, r});
    }
    printf("%lld", ans);
    return 0;
}

你可能感兴趣的:(ST表,堆/优先队列,贪心)