[BZOJ4293][PA2015]Siano
试题描述
农夫Byteasar买了一片n亩的土地,他要在这上面种草。
他在每一亩土地上都种植了一种独一无二的草,其中,第i亩土地的草每天会长高a[i]厘米。
Byteasar一共会进行m次收割,其中第i次收割在第d[i]天,并把所有高度大于等于b[i]的部分全部割去。Byteasar想知道,每次收割得到的草的高度总和是多少,你能帮帮他吗?
输入
第一行包含两个正整数n,m(1<=n,m<=500000),分别表示亩数和收割次数。
第二行包含n个正整数,其中第i个数为a[i](1<=a[i]<=1000000),依次表示每亩种植的草的生长能力。
接下来m行,每行包含两个正整数d[i],b[i](1<=d[i]<=10^12,0<=b[i]<=10^12),依次描述每次收割。
数据保证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
数据规模及约定
见“输入”
题解
可以发现,每次割完之后草的高度大小关系都是不变的,所以我们把草按照生长能力排序,然后就可以二分出每次割草的位置了,那么每次割草就是一个 [x, n] 的区间修改操作,线段树即可。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define LL long long
LL read() {
LL x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
#define maxn 500010
int n, A[maxn];
LL tagd[maxn<<2], tagh[maxn<<2], sumv[maxn<<2], minv[maxn<<2], maxv[maxn<<2], pre[maxn];
void maintain(int o, int l, int r) {
int lc = o << 1, rc = lc | 1;
if(!tagd[o]) sumv[o] = sumv[lc] + sumv[rc], minv[o] = minv[lc], maxv[o] = maxv[rc];
else {
sumv[o] = -(pre[r] - pre[l-1]) * tagd[o] + tagh[o] * (r - l + 1);
minv[o] = -tagd[o] * A[l] + tagh[o];
maxv[o] = -tagd[o] * A[r] + tagh[o];
}
return ;
}
void pushdown(int o, int l, int r) {
if(l == r) return maintain(o, l, r);
if(!tagd[o]) return maintain(o, l, r);
// puts("real pushdown");
int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
tagd[lc] = tagd[rc] = tagd[o];
tagh[lc] = tagh[rc] = tagh[o];
sumv[lc] = -(pre[mid] - pre[l-1]) * tagd[o] + tagh[o] * (mid - l + 1);
sumv[rc] = -(pre[r] - pre[mid]) * tagd[o] + tagh[o] * (r - mid);
minv[lc] = -tagd[o] * A[l] + tagh[o];
minv[rc] = -tagd[o] * A[mid+1] + tagh[o];
maxv[lc] = -tagd[o] * A[mid] + tagh[o];
maxv[rc] = -tagd[o] * A[r] + tagh[o];
tagd[o] = tagh[o] = 0;
return maintain(o, l, r);
}
LL query(int o, int l, int r, LL day, LL hei) {
pushdown(o, l, r);
if(minv[o] + day * A[l] >= hei) {
// printf("%d [%d, %d]: %lld\n", o, l, r, minv[o] + day * A[l]);
LL ans = sumv[o] + day * (pre[r] - pre[l-1]) - hei * (r - l + 1);
tagd[o] = day; tagh[o] = hei;
return maintain(o, l, r), ans;
}
if(l == r) return maintain(o, l, r), 0;
int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
LL ans = query(rc, mid + 1, r, day, hei);
if(maxv[lc] + day * A[mid] >= hei) ans += query(lc, l, mid, day, hei);
return maintain(o, l, r), ans;
}
#define maxlen 10000010
char Out[maxlen];
int num[20], cntn, olen;
int main() {
n = read(); int q = read();
for(int i = 1; i <= n; i++) A[i] = read();
sort(A + 1, A + n + 1);
for(int i = 1; i <= n; i++) pre[i] = pre[i-1] + A[i];
while(q--) {
LL d = read(), h = read(), tmp = query(1, 1, n, d, h);
if(tmp) {
cntn = 0; while(tmp) num[cntn++] = tmp % 10, tmp /= 10;
for(int i = cntn - 1; i >= 0; i--) Out[olen++] = num[i] + '0';
} else Out[olen++] = '0';
Out[olen++] = '\n';
}
Out[--olen] = '\0';
puts(Out);
return 0;
}
貌似加了输出优化比不加还慢。。。