NOI2010 超級鋼琴

2022-09-12 10:42:14 字數 1785 閱讀 8351

傳送門

給乙個長度為\(n\)的序列\(a\),定義子區間價值\(w_=\sum_^a_i\)

要求選出\(k\)個互不相同的子區間,使選出的區間價值和最大。

首先,為了快速求出一段區間的和,我們預處理字首和

然後就是乙個很巧妙的技巧:

定義\(max_\)為以\(o\)為左端點,長度在區間\([l,r]\)以內的權值和最大的連續區間。

很顯然\(max_=max(sum(t)-sum(o - 1), t∈[l,r])\)

其中\(sum(x) = \sum_^a[i]\)

因為固定了o點,\(sum(o - 1)\)一定是確定的,所以我們相當於要求\(sum(t)\)在區間\([l,r]\)中的最大值。

不難想到用線段樹維護,但是因為沒有修改操作,可以直接用\(rmq\)

然後就是貪心了,維護乙個大根堆,每次詢問堆頂元素的權值,在將它從堆中刪除……

然後就愉快的\(wa\)成\(sb\)了……

分析錯誤原因,是我們沒有考慮以\(o\)為左端點的區間有可能有不止乙個區間可以為答案做出貢獻……

所以我們在刪除對頂元素時,還需要將剩餘部分插入堆

假設當前堆頂的元素為\(max_\)且區間長度為\(t\)時,取到最大值

在刪除後我們將\(max_\)與\(max_\)扔回堆中即可

#include using namespace std;

#define debug(...) fprintf(stderr, __va_args__)

#define mp make_pair

#define fst first

#define snd second

templateinline bool chkmin(t &a, const t &b)

templateinline bool chkmax(t &a, const t &b)

inline int read()

typedef long long ll;

typedef pairpii;

const int maxn = 2e6 + 10;

int n, l, r, k, a[maxn], ok;

namespace rmq

void init()

pow[0] = 1;

for (int i = 1; i <= 20; ++i) pow[i] = pow[i - 1] << 1;

for (int j = 1; j <= log[n]; ++j)

for (int i = 1; i <= n; ++i)

rmq[i][j] = max(rmq[i][j - 1], rmq[i + pow[j - 1]][j - 1]);

}}struct node

inline int query()

inline int where()

};priority_queueq;

int main()

; now.val = now.query();

q.push(now);

}ll ans = 0;

node nxt;

while(k--), nxt.val = nxt.query(), q.push(nxt);

if(now.where() < now.r)

nxt = (node), nxt.val = nxt.query(), q.push(nxt);

}printf("%lld\n", ans);

return 0;

}

NOI2010 超級鋼琴

傳送門 這個題有趣。巧妙地利用st表和堆 首先最暴力的我就不說了 第二個暴力就是主席樹 堆,預計得分70 80,時間o klog 2n std是用堆儲存可能的區間,然後用st表查詢區間最小值 因為其實如果知道區間右端點,再處理個字首和s 那麼就只要查詢區間最小值就可以了,可以st表o 1 做 inc...

NOI2010 超級鋼琴

求出字首和 對於每個結尾i,設現在取的區間是 j 1,i 則i r j i l,取出該區間sum j 的最小值,將sum i sum j 放入堆中 建立乙個大根堆,每次取出堆頂元素,將排名k 1,將sum i 區間第k小值放入堆中 求區間第k小可以用主席樹 直到取滿k次為止 include incl...

NOI2010 超級鋼琴

和有一道區間前k大異或很像。先求字首和,然後就變成右端點固定,取左端點的第k小問題,主席樹維護即可。並用堆維護前k個。ac pragma gcc optimize ofast funroll all loops include define int long long using namespace...