[bzoj4293]_[PA2015]Siano(线段树+二分)

4293: [PA2015]Siano

Time Limit: 30 Sec Memory Limit: 256 MB
Submit: 581 Solved: 199
[Submit][Status][Discuss]

Description

农夫 Byteasar B y t e a s a r 买了一片 n n 亩的土地,他要在这上面种草。
他在每一亩土地上都种植了一种独一无二的草,其中,第i亩土地的草每天会长高 ai a i 厘米。
Byteasar B y t e a s a r 一共会进行 m m 次收割,其中第i次收割在第 di d i 天,并把所有高度大于等于 bi b i 的部分全部割去。 Byteasar B y t e a s a r 想知道,每次收割得到的草的高度总和是多少,你能帮帮他吗?

Input

第一行包含两个正整数 n,m(1n,m500000) n , m ( 1 ≤ n , m ≤ 500000 ) ,分别表示亩数和收割次数。
第二行包含 n n 个正整数,其中第i个数为 ai(1ai1000000) a i ( 1 ≤ a i ≤ 1000000 ) ,依次表示每亩种植的草的生长能力。
接下来 m m 行,每行包含两个正整数di,bi(1di10120bi1012),依次描述每次收割。
数据保证 d1<d2<...<dm d 1 < d 2 < . . . < d m ,并且任何时刻没有任何一亩草的高度超过 1012 10 12

Output

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

Sample Input

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

Sample Output

6
6
18
0

HINT

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

[PA2015]Siano







解:

感觉自己是一个智障,前几天才看过一道类似的题,比赛场上疯狂RE。

今天调读入忘开 long long l o n g   l o n g 还调了仨小时。。。

其实挺简单的,按生长速度排序,可以发现在任何时刻草的高度都是按高度从小到大排序。我们每次查询修改就在线段树上二分到第一个大于该割的位置,然后查询答案,区间修改。

二分的写法需要注意,直接在线段树上二分,不要二分的时候在线段树上查询,不然会是 log2 l o g 2 的。

需要注意的就是,下传标记先下传赋值在下传加标记。

#include<iostream>  
#include<cstdio>  
#include<cstring>  
#include<algorithm>  
using namespace std;  
struct segment_tree{  
    int lson,rson,l,r;  
    long long num,lll,tag,plu;//lll记录这个区间最左边数的值  
}b[1000005];  

int n,m,cnt,root,zui;  
long long v[500005],qv[500005],las,d,x;  

void build(int &u,int l,int r)  
{  
    u=++cnt;  
    b[u].l=l,b[u].r=r;  
    b[u].tag=-1;  
    if(l==r) return;  
    int mid=(l+r)/2;  
    build(b[u].lson,l,mid);  
    build(b[u].rson,mid+1,r);  
}  

void update(int u)  
{  
    b[u].num=b[b[u].lson].num+b[b[u].rson].num;  
    b[u].lll=b[b[u].lson].lll;  
}  

void pushdown(int u)//先赋值,后区间加  
{  
    if(b[u].tag!=-1)  
    {  
        b[b[u].lson].num=(b[b[u].lson].r-b[b[u].lson].l+1)*b[u].tag;  
        b[b[u].rson].num=(b[b[u].rson].r-b[b[u].rson].l+1)*b[u].tag;  
        b[b[u].lson].tag=b[u].tag;b[b[u].lson].plu=0;  
        b[b[u].rson].tag=b[u].tag;b[b[u].rson].plu=0;  
        b[b[u].lson].lll=b[u].tag;b[b[u].rson].lll=b[u].tag;  
        b[u].tag=-1;  
    }  
    if(b[u].plu!=0)  
    {  
        b[b[u].lson].num+=b[u].plu*(qv[b[b[u].lson].r]-qv[b[b[u].lson].l-1]);  
        b[b[u].rson].num+=b[u].plu*(qv[b[b[u].rson].r]-qv[b[b[u].rson].l-1]);  
        b[b[u].lson].plu+=b[u].plu;  
        b[b[u].rson].plu+=b[u].plu;  
        b[b[u].lson].lll+=v[b[b[u].lson].l]*b[u].plu;  
        b[b[u].rson].lll+=v[b[b[u].rson].l]*b[u].plu;  
        b[u].plu=0;  
    }  
}  

long long query(int u,long long da,long long x)//区间赋值  
{  
    pushdown(u);  
    if(b[u].lll+v[b[u].l]*da>x)//如果最左边需要割,这个区间都要割  
    {  
        zui=min(zui,b[u].l-1);  
        b[u].num+=da*(qv[b[u].r]-qv[b[u].l-1]);  
        long long ret=b[u].num-x*(b[u].r-b[u].l+1);  
        b[u].num=x*(b[u].r-b[u].l+1);  
        b[u].tag=x;b[u].lll=x;b[u].plu=0;  
        return ret;  
    }  
    if(b[u].l==b[u].r) return 0;  
    if(b[b[u].rson].lll+v[b[b[u].rson].l]*da>=x)//如果右儿子最左边需要割,左儿子可能也需要割  
      return query(b[u].lson,da,x)+query(b[u].rson,da,x);  
    else return query(b[u].rson,da,x);//右儿子最左边都不割,左儿子肯定不割  
    update(u);  
}  

void modify(int u,int l,int r,long long da)//区间加,标记打过了几天  
{  
    pushdown(u);  
    if(l==b[u].l&&r==b[u].r)  
    {  
        b[u].num+=da*(qv[b[u].r]-qv[b[u].l-1]);  
        b[u].plu+=da;b[u].lll+=da*v[b[u].l];  
        return;  
    }  
    int mid=(b[u].l+b[u].r)/2;  
    if(r<=mid)  modify(b[u].lson,l,r,da);  
    else if(l>mid) modify(b[u].rson,l,r,da);  
    else modify(b[u].lson,l,mid,da),modify(b[u].rson,mid+1,r,da);  
    update(u);  
}  

int main()  
{  
    scanf("%d%d",&n,&m);  
    for(int i=1;i<=n;i++)  
      scanf("%lld",&v[i]);  
    sort(v+1,v+1+n);  
    for(int i=1;i<=n;i++)  
      qv[i]=qv[i-1]+v[i];  
    build(root,1,n);  
    for(int i=1;i<=m;i++)  
    {  
        zui=n;//zui用于记录区间加在哪里结束  
        scanf("%lld%lld",&d,&x);//mmp就是这个是longlong,mmp读入就longlong,mmpBZOJ交上去还不知道哪里wa了,mmp对拍了俩小时都没拍出来</span>  
        printf("%lld\n",query(root,d-las,x));  
        if(zui!=0) modify(root,1,zui,d-las);  
        las=d;  
    }  
}  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值