农夫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]<d[2]<...<d[m] d [ 1 ] < d [ 2 ] < . . . < d [ m ] ,并且任何时刻没有任何一亩草的高度超过10^12。
输出m行,每行一个整数,依次回答每次收割能得到的草的高度总和。
4 4
1 2 4 3
1 1
2 2
3 0
4 4
6
6
18
0
第1天,草的高度分别为1,2,4,3,收割后变为1,1,1,1。
第2天,草的高度分别为2,3,5,4,收割后变为2,2,2,2。
第3天,草的高度分别为3,4,6,5,收割后变为0,0,0,0。
第4天,草的高度分别为1,2,4,3,收割后变为1,2,4,3。
隔壁ShadyPi dalao的非时间戳版本线段树sto 传送门 orz, 蒟蒻并不会不打时间戳的办法…
当时在UESTC并没有打出这道题, 以为是树套树, 因为似乎需要维护很多东西, 于是打了30分的 O(nm) O ( n m ) 暴力(然而最后居然爆0了QAQ)
然而其实并不用树套树, 因为这里有一个隐藏的优秀的性质:生长速度快的草永远不会比生长速度慢的草矮。所以我们先将草按速度排序, 再将所有的草在一棵线段树建点, 那么可以确定的是每次割草的时候割下的都是一个连续区间的草。
那么如何知道哪一段区间需要被割下呢?暴力pushdown显然不现实。
我们可以维护每个区间的最大值和最小值及高度总和和速度总和, 设需割下的草的高度为height,若 min(valseg)>height m i n ( v a l s e g ) > h e i g h t , 则说明当前区间所有的草都需要被割下, 直接打上lazy标记, 更新区间的高度总和。若 max(valseg)<height m a x ( v a l s e g ) < h e i g h t ,则说明当前区间所有的草都不需要被割, 直接返回0。其他情况时我们pushdown一层递归处理。
值得注意的是, 因为可能有一段区间经过几段割草操作而没有被割, 而每次的总高度、增量需要我们维护上一次割草操作的时间, 但我们pushdown一层的时候需要最近一次成功被割后的时间(因为在此期间其实下一层的草全部都在生长), 所以我们还需要维护一个值las。
代码如下:
#include
#include
#include
#include
#include
#define R register
#define gc getchar()
#define W while
#define IN inline
#define MX 1000005
#define ll long long
template <typename T>
IN void in (T &x)
{
x = 0; R char c = gc;
W (!isdigit(c)) c = gc;
W (isdigit(c))
{x = (x << 1) + (x << 3), x += c - 48, c = gc;}
}
namespace SGT
{
#define ls now << 1
#define rs now << 1 | 1
int sped[MX];
int dot, q;
struct Node
{
ll las ,tim, cut, mx, mn, sum, spd;
//cut 记录 最近一次成功被割的高度,
//tim记录最近一次被割(不一定成功)的时间,
//las记录最近一次成功被割的时间,
//mx,mn记录区间最高草的高度与最矮草的高度
//sum记录区间草的高度的总和
//spd记录区间草生长速度的总和
}tree[MX << 1];
IN void pushup(const int &now)
{tree[now].spd = tree[ls].spd + tree[rs].spd;}
//
void build(const int &now, const int &lef, const int &rig)
{
tree[now].cut = -1;
if(lef == rig)
{
tree[now].spd = sped[lef];
return;
}
int mid = (lef + rig) >> 1;
build(ls, lef, mid);
build(rs, mid + 1, rig);
pushup(now);
}
IN void pushdown(const int &now, const int &lef, const int &rig)
{
int mid = (lef + rig) >> 1;
if(~tree[now].cut)
{//没有标记时记为-1, 补码为1111 1111, 取反后为0000 0000
tree[ls].cut = tree[rs].cut = tree[now].cut;
tree[ls].las = tree[rs].las = tree[ls].tim = tree[rs].tim = tree[now].las;
tree[ls].sum = tree[ls].cut * (mid - lef + 1);
tree[rs].sum = tree[rs].cut * (rig - mid);
tree[ls].mn = tree[rs].mn = tree[ls].mx = tree[rs].mx = tree[now].cut;
tree[now].cut = -1;
}
}
ll query (const int &now, const int &lef, const int &rig, const ll &ct, const ll &tm)
{
ll ret;
tree[now].sum += (tm - tree[now].tim) * tree[now].spd;
tree[now].mn += (tm - tree[now].tim) * sped[lef];
tree[now].mx += (tm - tree[now].tim) * sped[rig];
tree[now].tim = tm;
if(tree[now].mx <= ct) return 0;//情况一
else if(tree[now].mn > ct)
{//情况二
ret = tree[now].sum - ct * (rig - lef + 1);
tree[now].sum = ct * (rig - lef + 1);
tree[now].mx = tree[now].mn = ct;
tree[now].cut = ct;
tree[now].tim = tree[now].las = tm;
return ret;
}
pushdown(now, lef, rig);
int mid = (lef + rig) >> 1;
ret = query(ls, lef, mid, ct, tm);
ret += query(rs, mid + 1, rig, ct, tm);
tree[now].sum = tree[ls].sum + tree[rs].sum;
tree[now].mn = tree[ls].mn;//显然左儿子的最小值就是区间最小值
tree[now].mx = tree[rs].mx;//右儿子的最大值就是区间最大值
return ret;
}
}
using namespace SGT;
using std::sort;
int main(void)
{
ll day, bound;
in(dot), in(q);
for (R int i = 1; i <= dot; ++i) in(sped[i]);
sort(sped + 1, sped + 1 + dot);
build(1, 1, dot);
W (q--)
{
in(day), in(bound);
printf("%lld\n", query(1, 1, dot, bound, day));
}
}