題目描述
注:此題爲BZOJ的權限題目
Description
農夫Byteasar買了一片n畝的土地,他要在這上面種草。
他在每一畝土地上都種植了一種獨一無二的草,其中,第i畝土地的草每天會長高a[i]釐米。
Byteasar一共會進行m次收割,其中第i次收割在第d[i]天,並把所有高度大於等於b[i]的部分全部割去。Byteasar想知道,每次收割得到的草的高度總和是多少,你能幫幫他嗎?
Input
第一行包含兩個正整數n,m(1<=n,m<=500000),分別表示畝數和收割次數。
第二行包含n個正整數,其中第i個數爲ai,依次表示每畝種植的草的生長能力。
接下來m行,每行包含兩個正整數d[i],bi,依次描述每次收割。
數據保證d[1]
Output
輸出m行,每行一個整數,依次回答每次收割能得到的草的高度總和。
樣例輸入
4 4
1 2 4 3
1 1
2 2
3 0
4 4
樣例輸出
6
6
18
0
題目分析
就這麼說吧,我覺得這題的代碼難度比這題的思維難度要難。我們可以很容易的發現,如果我們首先將所有的土地按照雜草的生長速度排序之後進行求解得到的答案其實是不變的,那麼我們第一步先排序,然後我們發現無論生長到第幾天,雜草的高度序列總是不下降的,同理有割掉草之後,繼續生長雜草的序列仍然是不下降的,那麼我們可以很容易的得到算法:
- 首先生長後,在線段樹上二分找到我們另當前雜草大於詢問的位置
- 輸出=(所有雜草高度之和-(大於的土地數量*詢問的高度))
- 將得到的位置後面的雜草(因爲全部高於當前)賦值爲當前詢問的高度
這裏都看上去很正常,複雜度似乎也可以接受
我們可以發現,當子樹中存在一個
然後我們考慮當
當然對於左右區間搞完之後就可以將當前的設置爲
最艱難的部分已經過去push_up就和平時的push_up沒有什麼不同。可以開始寫了。
代碼
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 500000;
long long a[MAXN+10], up[MAXN+10];
int aa, cc;
long long bb;
struct Tree{
int l, r;
long long Max, sum, b, c;
bool aa;
void operator+=(const Tree& an){
if(an.aa){
b += an.b; c += an.c;
Max += an.b + an.c*a[r];
sum += an.b*(r-l+1)+an.c*(up[r]-up[l-1]);
}else{
aa = 0; b = an.b; c = an.c;
Max = b + c * a[r];
sum = b * (r - l + 1) + (up[r] - up[l-1]) * c;
}
}
}tree[MAXN*4+10];
void recalc(int u, int l, int r){
tree[u].sum = tree[u].sum * tree[u].aa + tree[u].b * (r - l + 1) + tree[u].c * (up[r] - up[l-1]);
tree[u].Max = tree[u].Max * tree[u].aa + tree[u].b + tree[u].c * up[r];
}
void push_down(int u){
tree[u*2+1] += tree[u];
tree[u*2] += tree[u];
tree[u].aa = 1, tree[u].b = 0, tree[u].c = 0;
}
void push_up(int u){
tree[u].Max = tree[u*2+1].Max;
tree[u].sum = tree[u*2].sum + tree[u*2+1].sum;
tree[u].aa = 1;
tree[u].b = tree[u].c = 0;
}
int Findpos(int u, long long v){
if(tree[u].l == tree[u].r) return tree[u].l;
push_down(u);
if(tree[u*2].Max >= v) return Findpos(u*2, v);
return Findpos(u*2+1, v);
}
long long query(int u, int pos){
if(pos <= tree[u].l) return tree[u].sum;
int mid=(tree[u].l+tree[u].r)>>1;
push_down(u);
long long ret = 0;
if(pos <= mid) ret += query(u*2, pos);
ret += query(u*2+1, pos);
return ret;
}
void reset(int u, int pos, long long v){
if(pos <= tree[u].l){
tree[u].aa = tree[u].c = 0; tree[u].b = v;
tree[u].Max = v;
tree[u].sum = (tree[u].r - tree[u].l + 1) * v;
return ;
}
int mid = (tree[u].l + tree[u].r) >> 1;
push_down(u);
reset(u*2+1, pos, v);
if(pos <= mid) reset(u*2, pos, v);
push_up(u);
}
void build(int u, int l, int r){
tree[u].l = l, tree[u].r = r;
tree[u].aa = 1;
if(l == r) return ;
int mid = (l + r) >> 1;
build(u*2, l, mid);
build(u*2+1, mid+1, r);
}
int main(){
int n, m;
long long d, b, ud=0;
scanf("%d%d", &n, &m);
build(1, 1, n);
for(int i=1;i<=n;i++) scanf("%I64d", &a[i]);
sort(a+1, a+1+n);
for(int i=1;i<=n;i++) up[i] = up[i-1] + a[i];
for(int i=1;i<=m;i++){
scanf("%I64d%I64d", &d, &b);
long long day = d - ud;
tree[1].c += day;
tree[1].Max += day * a[n];
tree[1].sum += day * up[n];
if(tree[1].Max <= b){
ud = d;
printf("0\n");
continue;
}
long long pos = Findpos(1, b);
printf("%I64d\n", query(1, pos) - 1LL * b * (n-pos+1));
reset(1, pos, b);
ud = d;
}
return 0;
}