割韭菜<线段树>

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

小王家的后院有一块菜园,并且小王非常的喜欢吃韭菜,这天小王打算在自家的菜园种植韭菜。

起初小王种植了n棵韭菜的种子,此时n棵韭菜的高度都为0,由于小王每天都会给所有的韭菜施肥,所以韭菜生长的速度飞快,但因为韭菜种子质量不同,施肥后每颗韭菜每天以固定a[i]的速度生长。

接下来的日子里,小王有m天想要吃韭菜,对于第D[i]天小王会把所有高度高于B[i]的韭菜收割到B[i],小王想知道每次他收割韭菜的总收割量。
 

输入描述:

 

第一行给定一个t,代表t组测试数据。(1<=t<=10)

对于每组数据,给定n,m代表有n棵韭菜,m次收割操作。

(1<=n,m<=1e5,∑n,∑m<=5e5)

接下来一行,给定每棵韭菜每天可以增长的高度a[i]。(1<=a[i]<=1e5)

下面m行,每行给定D[i],B[i],代表第D[i]天收割高于B[i]的韭菜,D[i]保证单调递增。(1<=D[i]<=1e9,1<=B[i]<=1e9)

(数据保证没有任何时刻韭菜高度的总和超出long long范围)

输出描述:

 

对于每组测试数据输出m行,每行代表收割得到韭菜高度的总和。
 

示例1

输入

1
4 4
1 3 4 2
1 1
2 0
3 3
4 1000

输出

6
14
1
0

查询时只需要判断区间的最后一棵韭菜的高度a与B的关系,a<=B结束,a>B时,若左儿子的a<=Ba则直接查询右区间,若左儿子的a>Ba直接加上右区间的贡献,继续查询左儿子即可。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N=100010;

ll a[N];
struct node
{
    int l,r;
    ll a,b,c,t,H,T;//a最右即最高 b总高度 c总生长速度 t上次收割时间 H T为收割懒标记 高度和时间
}tr[N*4];

void pushup(int u)
{
    tr[u].a=tr[u<<1|1].a;
    tr[u].b=tr[u<<1|1].b+tr[u<<1].b+(tr[u<<1|1].t-tr[u<<1].t)*tr[u<<1].c;
    tr[u].t=tr[u<<1|1].t;
}

void modify(int u,ll T,ll H)
{
    tr[u].H=H;
    tr[u].T=T;
    tr[u].a=H;
    tr[u].b=H*(tr[u].r-tr[u].l+1);
    tr[u].t=T;
}

void pushdown(int u)
{
    if(tr[u].T)
    {
        modify(u<<1,tr[u].T,tr[u].H);
        modify(u<<1|1,tr[u].T,tr[u].H);
        tr[u].T=tr[u].H=0;
    }
}

void build(int u,int l,int r)
{
    if(l==r) tr[u]={l,r,0,0,a[r],0,0,0};
    else
    {
        tr[u]={l,r,0,0,0,0,0,0};
        int mid=l+r>>1;
        build(u<<1,l,mid);
        build(u<<1|1,mid+1,r);
        
        tr[u].c=tr[u<<1].c+tr[u<<1|1].c;
    }
}

ll query(int u,ll T,ll H)
{
    ll ans=0;
    if(tr[u].l==tr[u].r)
    {
        if(tr[u].a+tr[u].c*(T-tr[u].t)>H)
        {
            ans=tr[u].a+tr[u].c*(T-tr[u].t)-H;
            modify(u,T,H);
            return ans;
        }
        else return 0;
    }
    
    pushdown(u);
    if(tr[u<<1|1].a+a[tr[u<<1|1].r]*(T-tr[u<<1|1].t)<=H) return 0;
    else if(tr[u<<1].a+a[tr[u<<1].r]*(T-tr[u<<1].t)<=H) ans=query(u<<1|1,T,H);
    else 
    {
        ans=query(u<<1,T,H)+tr[u<<1|1].b+(T-tr[u<<1|1].t)*tr[u<<1|1].c-(tr[u<<1|1].r-tr[u<<1|1].l+1)*H;
        modify(u<<1|1,T,H);
    }
    
    pushup(u);
    return ans;
}

int main()
{
    int t;
    scanf("%d",&t);
    
    while(t--)
    {
        int n,q;
        scanf("%d%d",&n,&q);
        
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
        sort(a+1,a+1+n);
        build(1,1,n);
        ll H,T;
        for(int i=1;i<=q;i++)
        {
            scanf("%lld%lld",&T,&H);
            ll res=query(1,T,H);
            printf("%lld\n",res);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值