【线段树】【二分】[PA2015][BZOJ4293]Siano

题目

Description

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

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

输出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。

Source

By Claris

分析

在任何时刻,生长速度较快的草的高度都不会低于生长速度较慢的草的高度。
我们把草按照生长速度排序,然后就可以用线段树来维护这些草的高度。
然后,我们每次在线段树中查找高度大于b的部分(也就是线段树上的二分),求出(高度-b)的和再减去,就可以得到答案。

#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 500000
typedef long long LL;
int n,a[MAXN+10],m;
template<class T>
void Read(T &x){
    char c;
    while(c=getchar(),c!=EOF)
        if(c>='0'&&c<='9'){
            x=c-'0';
            while(c=getchar(),c>='0'&&c<='9')
                x=x*10+c-'0';
            ungetc(c,stdin);
            return;
        }
}
struct node{
    LL a,sum,tag,ld,tagd,mx,mi;// a:生长速度的总和 sum:高度和 tag懒标记 tagd:打懒标记的时间 mx:区间最大草的高度 mi:区间最小草的高度
}tree[MAXN*4+10];
void build(int i,int l,int r){
    tree[i].tag=-1;
    if(l==r){
        tree[i].a=a[l];
        return;
    }
    int mid=(l+r)>>1;
    build(i<<1,l,mid);
    build((i<<1)|1,mid+1,r);
    tree[i].a=tree[i<<1].a+tree[(i<<1)|1].a;
}
inline void push_down(int i,int l,int r){
    int mid=(l+r)>>1;
    if(~tree[i].tag){
        tree[i<<1].sum=tree[i].tag*(mid-l+1);
        tree[i<<1].mi=tree[i<<1].mx=tree[(i<<1)|1].mi=tree[(i<<1)|1].mx=tree[i].tag;
        tree[(i<<1)|1].sum=tree[i].tag*(r-mid);
        tree[i<<1].tagd=tree[(i<<1)|1].tagd=tree[i<<1].ld=tree[(i<<1)|1].ld=tree[i].tagd;
        tree[i<<1].tag=tree[(i<<1)|1].tag=tree[i].tag;
        tree[i].tag=-1;
    }
}
LL get_sum(int i,int l,int r,LL d,LL b){
    LL ret;
    tree[i].sum+=(d-tree[i].ld)*tree[i].a;
    tree[i].mi+=a[l]*(d-tree[i].ld);
    tree[i].mx+=a[r]*(d-tree[i].ld);
    tree[i].ld=d;
    if(tree[i].mx<=b)
        return 0;
    if(tree[i].mi>b){
        ret=tree[i].sum-b*(r-l+1);
        tree[i].tagd=tree[i].ld=d;
        tree[i].tag=b;
        tree[i].sum=(r-l+1)*b;
        tree[i].mx=tree[i].mi=b;
        return ret;
    }
    push_down(i,l,r);
    int mid=(l+r)>>1;
    ret=get_sum(i<<1,l,mid,d,b)+get_sum((i<<1)|1,mid+1,r,d,b);
    tree[i].sum=tree[i<<1].sum+tree[(i<<1)|1].sum;
    tree[i].mi=tree[i<<1].mi,tree[i].mx=tree[(i<<1)|1].mx;
    return ret;
}
void read(){
    Read(n),Read(m);
    for(int i=1;i<=n;i++)
        Read(a[i]);
    sort(a+1,a+n+1);
    build(1,1,n);
}
void solve(){
    int i;
    LL ans,d,b;
    for(i=1;i<=m;i++){
        Read(d),Read(b);
        ans=get_sum(1,1,n,d,b);
        printf("%lld\n",ans);
    }
}
int main()
{
    read();
    solve();
}

你可能感兴趣的:(C++,线段树,二分,bzoj,PA2015)