【线段树】【树】[BZOJ4293][HYSBZ4293][PA2015]Siano
题目描述
注:此题为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;
}