BZOJ4293【PA2015】Siano <线段树>

Problem

【PA2015】Siano

Time  Limit:  30  Sec\mathrm{Time\;Limit:\;30\;Sec}
Memory  Limit:  256  MB\mathrm{Memory\;Limit:\;256\;MB}

Description

农夫Byteasar\mathrm{Byteasar}买了一片nn亩的土地,他要在这上面种草。
他在每一亩土地上都种植了一种独一无二的草,其中,第ii亩土地的草每天会长高aia_i厘米。
Byteasar\mathrm{Byteasar}一共会进行mm次收割,其中第ii次收割在第did_i天,并把所有高度大于等于bib_i的部分全部割去。
Byteasar\mathrm{Byteasar}想知道,每次收割得到的草的高度总和是多少,你能帮帮他吗?

Input

第一行包含两个正整数n,mn,m,分别表示亩数和收割次数。
第二行包含nn个正整数,其中第ii个数为aia_i,依次表示每亩种植的草的生长能力。
接下来mm行,每行包含两个正整数di,bid_i,b_i,依次描述每次收割。

Output

输出mm行,每行一个整数,依次回答每次收割能得到的草的高度总和。

Sample Input

1
2
3
4
5
6
4 4
1 2 4 3
1 1
2 2
3 0
4 4

Sample Output

1
2
3
4
6
6
18
0

Explanation

11天,草的高度分别为1,2,4,31,2,4,3,收割后变为1,1,1,11,1,1,1
22天,草的高度分别为2,3,5,42,3,5,4,收割后变为2,2,2,22,2,2,2
33天,草的高度分别为3,4,6,53,4,6,5,收割后变为0,0,0,00,0,0,0
44天,草的高度分别为1,2,4,31,2,4,3,收割后变为1,2,4,31,2,4,3

HINT

1n,m5×1051\le n,m\le5\times10^5, 1ai1061\le a_i\le10^6, 1di,bi10121\le d_i,b_i\le10^{12}
数据保证d1<d2<<dmd_1<d_2<\cdots<d_m,并且任何时刻没有任何一亩草的高度超过101210^12

Source

By Claris

标签:线段树

Solution

比较灵活的线段树。

观察题目可以发现一个性质,即长速快的在任意时刻都比长速慢的高度高。这是由于每次修剪都是将所有的剪到同一高度,这样长速快的在修剪后的高度一定大于等于长速慢的在修剪后的高度。

将长速从低到高排序,不会影响询问,并且每次询问剪去的部分一定是一个后缀。那么可以在线段树上分治寻找剪和不剪的分界点,同时累加答案。

对于每个区间需要维护其长速之和、高度和两个基础元素。为了方便询问,需要维护高度的最大和最小值,即该区间最右边和最左边的苗的高度。这样如果当前区间的最大高度mxbmx\le b,可知不用继续递归;如果当前区间最小高度mibmi\ge b,可知整个区间都要修剪,打区间标记后返回。而对于标记,每个区间需要维护三个元素tc,td,ldtc,td,ld,分别表示该区间中的所有高度均变为tctc,这个变化发生在tdtd时刻,上一次递归到该区间的时间是ldld。询问每次递归进入一个区间先计算从上次递归到该区间也就是ldld到现在总共长了多少,更新信息。注意tdtdldld不能合并为一个变量,这是由于该区间上次被递归到时可能先更新了信息,但是并未打标记,即其下面的子区间没有更新信息,故tdtdldld不同。

一棵线段树即可维护,某些细节注意一下即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <bits/stdc++.h>
#define MAX_N 500000
#define mid ((s+t)>>1)
using namespace std;
typedef long long lnt;
template <class T> inline void read(T &x) {
x = 0; int c = getchar(), f = 1;
for (; !isdigit(c); c = getchar()) if (c == 45) f = -1;
for (; isdigit(c); c = getchar()) (x *= 10) += f*(c-'0');
}
int n, m; lnt a[MAX_N+5], b, d;
struct node {lnt v, s, tc, td, ld, lv, rv;} tr[MAX_N<<2];
void update(int v) {
tr[v].v = tr[v<<1].v+tr[v<<1|1].v;
tr[v].s = tr[v<<1].s+tr[v<<1|1].s;
tr[v].lv = tr[v<<1].lv, tr[v].rv = tr[v<<1|1].rv;
}
void downtag(int v, int s, int t) {
if (tr[v].tc == -1) return;
tr[v<<1].s = tr[v].tc*(mid-s+1);
tr[v<<1|1].s = tr[v].tc*(t-mid);
tr[v<<1].lv = tr[v<<1].rv = tr[v].tc;
tr[v<<1|1].lv = tr[v<<1|1].rv = tr[v].tc;
tr[v<<1].tc = tr[v<<1|1].tc = tr[v].tc;
tr[v<<1].td = tr[v<<1|1].td = tr[v].td;
tr[v<<1].ld = tr[v<<1|1].ld = tr[v].td;
tr[v].tc = -1;
}
void build(int v, int s, int t) {
tr[v].tc = -1; if (s == t) {tr[v].v = a[s]; return;}
build(v<<1, s, mid), build(v<<1|1, mid+1, t), update(v);
}
lnt query(int v, int s, int t) {
lnt ret = 0LL; tr[v].s += (d-tr[v].ld)*tr[v].v;
tr[v].lv += a[s]*(d-tr[v].ld), tr[v].rv += a[t]*(d-tr[v].ld);
if (tr[v].lv >= b) {
ret = tr[v].s-b*(t-s+1), tr[v].s = b*(t-s+1);
tr[v].lv = tr[v].rv = tr[v].tc = b, tr[v].td = d;
} else if (tr[v].rv > b) {
downtag(v, s, t);
ret += query(v<<1, s, mid);
ret += query(v<<1|1, mid+1, t);
update(v);
}
return tr[v].ld = d, ret;
}
int main() {
read(n), read(m);
for (int i = 1; i <= n; i++) read(a[i]);
sort(a+1, a+n+1), build(1, 1, n);
while (m--) read(d), read(b), printf("%lld\n", query(1, 1, n));
return 0;
}