傳送門
給乙個長度為\(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...