[UESTC SC T4] Chika 的烦恼

40 篇文章 0 订阅
15 篇文章 0 订阅

BZOJ链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4293

Chika的烦恼

【题目描述】

Chika 家经营着一家旅馆,她经常要帮家里的忙,有时候,她需要去清理长
满杂草的后院。具体地,后院里面的每棵草都有其生长速率(每天所能生长的高
度),在某些给定的日期,她需要把高度大于某个值b 的杂草割掉一截,使其高
度剩余b。(b 在每次给定的日期时不一定相同。)
她想请你帮她统计一下,每次她所割掉的草的总量是多少。
假设最开始每棵草的高度是零。
【输入格式】
第一行为两个整数n, m,代表后院里草总共有n 棵,以及发生的事件的总
数m。
第二行包含n 个整数a1, a2, …, an。ai (1≤i≤n)代表第i 棵草的生长速
度为每天ai 个单位。
接下来m 行,第i 行包含两个整数di, bi,代表在第di 天结束时,Chika 把
所有高度大于bi 的草的高度变成了bi。(保证输入的di 是递增的)
【输出格式】
包含m 行,每行一个整数,代表割掉的草的总量共有多少个单位。
【样例输入】
4 4
1 2 4 3
1 1
2 2
3 0
4 4
【样例输出】
6
6
18
0
【数据范围】
1≤n,m≤400000,
1≤ai≤ 106 10 6 ,
1≤di≤ 1012 10 12 ,
6
0≤bi≤ 1012 10 12 ,
d1

样例解释

第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。

题解

电子科大居然出BZOJ原题。。。然而我之前并不知道,打暴力还没开long long,爆零。。。

我们可以发现,生长速度快的草的高度不可能低于长得慢的草的高度,所以如果我们将所有草按生长速度排一次序,我们可以发现每次剪掉的草都是在一个区间内的。

因此我们可以建立一棵线段树来维护整片草坪,线段树维护区间加编号,区间赋值编号,区间最大值(即这个区间的右边界),还有区间和。

区间加操作我们加的是时间,在更新区间和的时候我们只需用这个时间加上这个区间的总生长速度,区间的生长速度可以求一波前缀和来做到 O(1) O ( 1 ) 调用。

剪草的过程就是区间赋值的过程,我们在赋值的时候就可以顺便记录下来剪掉了多少草。

在维护标记的时候,要先push赋值标记,再push区间加标记,当有新的赋值标记从上面传下来的时候,就把区间加标记清空。

其实都是线段树的基本操作。。。只是博主线段树写得少所以调了一上午而已。。。

另外,因为每次区间加都是针对整个区间的,所以不需要专门写个区间加。

LPA大佬维护了时间戳的线段树,蒟蒻膜拜。

代码
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int M=5e5+5;
ll n,m,date,s[M];
struct node{
    ll rb,add,sum,cut;
    node(){cut=-1ll;}
};
node tree[M<<2];
void in()
{
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;++i)
    scanf("%lld",&s[i]);
}
void up(int v)
{
    tree[v].sum=tree[v<<1].sum+tree[v<<1|1].sum;
    tree[v].rb=tree[v<<1|1].rb;
}
void pushadd(int v,int le,int ri)
{
    int v1=v<<1,v2=v<<1|1;
    tree[v1].add+=tree[v].add;
    tree[v2].add+=tree[v].add;
    int mid=(le+ri)>>1;
    tree[v1].sum+=(s[mid]-s[le-1])*tree[v].add;
    tree[v2].sum+=(s[ri]-s[mid])*tree[v].add;
    tree[v1].rb+=(s[mid]-s[mid-1])*tree[v].add;
    tree[v2].rb+=(s[ri]-s[ri-1])*tree[v].add;
    tree[v].add=0;
}
void pushcut(int v,int le,int ri)
{
    int v1=v<<1,v2=v<<1|1;
    tree[v1].cut=tree[v2].cut=tree[v].cut;
    int mid=(le+ri)>>1;
    tree[v1].sum=(mid-le+1)*tree[v].cut;
    tree[v2].sum=(ri-mid)*tree[v].cut;
    tree[v1].rb=tree[v2].rb=tree[v].cut;
    tree[v].cut=-1;
    tree[v<<1].add=tree[v<<1|1].add=0;
}
void push(int v,int le,int ri)
{
    if(~tree[v].cut)pushcut(v,le,ri);
    if(tree[v].add)pushadd(v,le,ri);
}
void add(ll ad)
{
    tree[1].add+=ad;
    tree[1].sum+=s[n]*ad;
    tree[1].rb+=(s[n]-s[n-1])*ad;
}
ll cut(int v,int le,int ri,int lb,int rb,ll cu)
{
    if(lb<=le&&ri<=rb)
    {
        tree[v].cut=cu;
        tree[v].add=0;
        ll k=tree[v].sum;
        tree[v].sum=(ri-le+1)*cu;
        tree[v].rb=cu;
        return k-tree[v].sum;
    }
    push(v,le,ri);
    int mid=(le+ri)>>1;
    ll ans=0;
    if(lb<=mid)ans=cut(v<<1,le,mid,lb,rb,cu);
    if(mid<rb)ans+=cut(v<<1|1,mid+1,ri,lb,rb,cu);
    up(v);
    return ans;
}
int get(int v,int le,int ri,ll b)
{
    if(le==ri)return tree[v].sum<b?0:le;
    push(v,le,ri);
    int mid=(le+ri)>>1;
    if(tree[v<<1].rb>=b)return get(v<<1,le,mid,b);
    else return get(v<<1|1,mid+1,ri,b);
}
ll work(ll a,ll b)
{
    add(a-date);
    int pos=get(1,1,n,b);
    if(!pos)return 0;
    return cut(1,1,n,pos,n,b);
}
void ac()
{
    sort(s+1,s+1+n);
    for(int i=2;i<=n;++i)
    s[i]+=s[i-1];
    ll a,b;
    for(int i=1;i<=m;++i)
    {
        scanf("%lld%lld",&a,&b);
        printf("%lld\n",work(a,b));
        date=a;
    }
}
int main()
{
    in();ac();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值