线段树二分 - Luogu P5579 [PA2015] - Siano
线段树二分 - Luogu P5579 [PA2015] - Siano
经过排序后,每次操作都可以转化为一个连续区间。在割草时,只需要查询最小的高于v的草,然后修改从它开始的整个后缀区间。本题难点在于如果查询最小的高于v的草,如果简单的用二分内套线段树单点查询,是logn * logn
的复杂度,会T。如果在线段树中维护区间最小草长,可以写出logn
的算法来实现这个查询。(见find
函数)。
本题一切均建立在这样一个特殊条件下:线段树维护的区间是非递减区间
#include <bits/stdc++.h>
using namespace std;
const int N = 5+10;
typedef long long ll;
int n, m;
ll k, v;
ll a[N];
struct Node{
ll inc; // 该区间的单日增长
ll sum; // 该区间的总长度
ll minv; // 该区间的最小草长 = 左子区间的最小草长
ll lazy; // 增长天数
ll setlazy; // 剪为v
}tr[N<<2];
void build(int rt, int l, int r){
tr[rt].sum = 0;
tr[rt].minv = 0;
tr[rt].lazy = 0;
tr[rt].setlazy = -1;
if(l==r){
tr[rt].inc = a[l];
}else{
int mid = l+r>>1;
build(rt<<1, l, mid);
build(rt<<1|1, mid+1, r);
tr[rt].inc = tr[rt<<1].inc + tr[rt<<1|1].inc;
}
}
void push_up(int rt){
tr[rt].sum = tr[rt<<1].sum + tr[rt<<1|1].sum;
tr[rt].minv = tr[rt<<1].minv;
}
void push_down(int rt, int l, int r){
if(tr[rt].setlazy != -1){
int mid = l+r>>1;
tr[rt<<1].sum = (mid-l+1) * tr[rt].setlazy;
tr[rt<<1|1].sum = (r-mid) * tr[rt].setlazy;
tr[rt<<1].minv = tr[rt<<1|1].minv = tr[rt].setlazy;
tr[rt<<1].setlazy = tr[rt<<1|1].setlazy = tr[rt].setlazy;
tr[rt<<1].lazy = tr[rt<<1|1].lazy = 0;
tr[rt].setlazy = -1;
}
if(tr[rt].lazy){
int mid = l+r>>1;
tr[rt<<1].sum += tr[rt<<1].inc * tr[rt].lazy;
tr[rt<<1|1].sum += tr[rt<<1|1].inc * tr[rt].lazy;
tr[rt<<1].lazy += tr[rt].lazy;
tr[rt<<1|1].lazy += tr[rt].lazy;
tr[rt<<1].minv += a[l] * tr[rt].lazy;
tr[rt<<1|1].minv += a[mid+1] * tr[rt].lazy;
tr[rt].lazy = 0;
}
}
int find(ll v){
int rt = 1;
int l = 1, r = n;
int ans = -1;
while(l <= r){
push_down(rt, l, r);
int mid = l+r>>1;
if(l==r){
if(tr[rt].minv > v) ans = l;
break;
}
if(tr[rt<<1|1].minv > v){
ans = mid+1;
rt = rt<<1;
r = mid;
}else{
rt = rt<<1|1;
l = mid+1;
}
}
return ans;
}
ll query(int rt, int l, int r, int ql, int qr){
if(ql <= l && qr >= r){
return tr[rt].sum;
}else{
push_down(rt, l, r);
int mid = l+r>>1;
ll ans = 0;
if(ql <= mid) ans += query(rt<<1, l, mid, ql, qr);
if(qr > mid) ans += query(rt<<1|1, mid+1, r, ql, qr);
return ans;
}
}
void cut(int rt, int l, int r, int cl, int cr, ll v){
if(cl <= l && cr >= r){
tr[rt].sum = v * (r-l+1);
tr[rt].minv = v;
tr[rt].lazy = 0;
tr[rt].setlazy = v;
// 置位操作会清楚当前节点的+操作
// 在pushdown的时候先置位再+
// 如果+lazy不为0,说明是先置位再+
}else{
int mid = l+r>>1;
push_down(rt, l, r);
if(cl <= mid){
cut(rt<<1, l, mid, cl, cr, v);
}
if(cr > mid){
cut(rt<<1|1, mid+1, r, cl, cr, v);
}
push_up(rt);
}
}
int main(){
// freopen("C:\\Users\\13234\\Desktop\\files\\IO\\0.in","r",stdin);
// freopen("C:\\Users\\13234\\Desktop\\files\\IO\\0.out","w",stdout);
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i){
scanf("%d", &a[i]);
}
sort(a+1, a+1+n);
build(1, 1, n);
ll prek = 0;
while(m--){
scanf("%lld%lld", &k, &v);
ll c = k - prek;
prek = k;
push_down(1, 1, n);
tr[1].sum += tr[1].inc * c;
tr[1].minv += a[1] * c;
tr[1].lazy += c;
int p = find(v);
if(p == -1){
printf("0\n");
continue;
}
ll res = query(1, 1, n, p, n) - (n-p+1) * v;
cut(1, 1, n, p, n, v);
printf("%lld\n", res);
}
return 0;
}
---- suffer now and live the rest of your life as a champion ----