注:此题为BZOJ的权限题目
农夫Byteasar买了一片n亩的土地,他要在这上面种草。
他在每一亩土地上都种植了一种独一无二的草,其中,第i亩土地的草每天会长高a[i]厘米。
Byteasar一共会进行m次收割,其中第i次收割在第d[i]天,并把所有高度大于等于b[i]的部分全部割去。Byteasar想知道,每次收割得到的草的高度总和是多少,你能帮帮他吗?
第一行包含两个正整数n,m(1<=n,m<=500000),分别表示亩数和收割次数。
第二行包含n个正整数,其中第i个数为ai,依次表示每亩种植的草的生长能力。
接下来m行,每行包含两个正整数d[i],bi,依次描述每次收割。
数据保证d[1]
输出m行,每行一个整数,依次回答每次收割能得到的草的高度总和。
4 4
1 2 4 3
1 1
2 2
3 0
4 4
6
6
18
0
就这么说吧,我觉得这题的代码难度比这题的思维难度要难。我们可以很容易的发现,如果我们首先将所有的土地按照杂草的生长速度排序之后进行求解得到的答案其实是不变的,那么我们第一步先排序,然后我们发现无论生长到第几天,杂草的高度序列总是不下降的,同理有割掉草之后,继续生长杂草的序列仍然是不下降的,那么我们可以很容易的得到算法:
这里都看上去很正常,复杂度似乎也可以接受 O(nlogn) ,那么我们开始写。。。写着写着我们发现如果要统计区间每一天增加多少怎么办。。。可以统计当前给定 Ai 的前缀和 di 对于一个区间 [l,r] 我们有每天生长 (dr−dl−1) 那么就可以得到 c 天后生长多少了,但是我们同样可以发现,我们还需要维护一个 b 表示当前的树我们需要赋值成多少。。。可以写了?不能
我们可以发现,当子树中存在一个 b 同时当前也有一个 c 要push_down的时候,我们两边都不能抛弃那么我们令 a 表示是否保留当前值那么我们对于每一段就有
然后我们考虑当 tree[u].a==1 的情况,我们可以发现 1 代表保留原来的内容,那么我们保持原来的 a 不变, b、c 累加(因为满足分配律)所以我们可以得到
当然对于左右区间搞完之后就可以将当前的设置为
#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;
}