• 【BZOJ】4293: [PA2015]Siano 线段树上二分


    【题意】给定n棵高度初始为0的草,每天每棵草会长高a[i],m次收割,每次在d[i]天将所有>b[i]的草收割到b[i],求每次收割量。n<=500000。

    【算法】线段树上二分

    【题解】按照生长速度a[]排序后,容易发现数列永远单调。

    在线段树上的区间维护以下值:

    1.最后一棵草的高度a

    2.上次收割日期b

    3.总的草高和c

    4.总的生长速度和d

    5.收割标记D和B

    上传的时候注意右区间收割晚于左区间时强制合并。

    下传的时候注意标记D和B直接覆盖。

    线段树上二分:

    1.判断当前区间是否符合(一般为区间最右端点),否则返回r+1

    2.若l=r,返回。

    3.查询左区间。

    4.若左区间不符合,查询右区间。

    过程中可以顺便查询答案和修改标记。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cctype>
    #define int long long
    using namespace std;
    int read(){
        int s=0,t=1;char c;
        while(!isdigit(c=getchar()))if(c=='-')t=-1;
        do{s=s*10+c-'0';}while(isdigit(c=getchar()));
        return s*t;
    }
    const int maxn=500010;
    int a[maxn],sum,n,m;
    struct tree{int l,r,a,b,c,d,B,D;}t[maxn*4];
    void modify(int k,int B,int D){t[k].B=B;t[k].D=D;t[k].a=B;t[k].b=D;t[k].c=B*(t[k].r-t[k].l+1);}
    void up(int k){
        t[k].a=t[k<<1|1].a;
        t[k].b=t[k<<1|1].b;
        t[k].c=t[k<<1].c+t[k<<1].d*(t[k<<1|1].b-t[k<<1].b)+t[k<<1|1].c;
    }
    void down(int k){
        if(t[k].D){
            modify(k<<1,t[k].B,t[k].D);modify(k<<1|1,t[k].B,t[k].D);
            t[k].B=t[k].D=0;
        }
    }
    void build(int k,int l,int r){
        t[k].l=l;t[k].r=r;
        if(l==r){t[k].a=0;t[k].b=0;t[k].c=0;t[k].d=a[l];return;}
        int mid=(l+r)>>1;
        build(k<<1,l,mid);build(k<<1|1,mid+1,r);
        up(k);
        t[k].d=t[k<<1].d+t[k<<1|1].d;
    }
    void plus(int k,int D,int B){sum+=t[k].c+t[k].d*(D-t[k].b)-B*(t[k].r-t[k].l+1);modify(k,B,D);}
    int find(int k,int D,int B){
        if(t[k].a+a[t[k].r]*(D-t[k].b)<=B)return t[k].r+1;
        if(t[k].l==t[k].r){
            plus(k,D,B);
            return t[k].r;
        }
        down(k);
        int num;
        if((num=find(k<<1,D,B))<=t[k<<1].r){
            plus(k<<1|1,D,B);
        }
        else num=find(k<<1|1,D,B);
        up(k);
        return num;
    }
    #undef int
    int main(){
    #define int long long
        n=read();m=read();
        for(int i=1;i<=n;i++)a[i]=read();
        sort(a+1,a+n+1);
        build(1,1,n);
        while(m--){
            int D=read(),B=read();
            sum=0; 
            find(1,D,B);
            printf("%lld
    ",sum);
        }
        return 0;
    }
    View Code

    写复杂的题目前一定要列好程序草稿,把细节都写清楚,程序写出来就会比较清晰,不容易犯错。

    原来这么复杂的题目也是可以1A的(躺

  • 相关阅读:
    Android Volley入门到精通:定制自己的Request
    Android高效加载大图、多图解决方案,有效避免程序OOM
    Android Volley入门到精通:使用Volley加载网络图片
    Android Volley入门到精通:初识Volley的基本用法
    彻底理解ThreadLocal
    Android中Parcelable接口用法
    Handler详解系列(四)——利用Handler在主线程与子线程之间互发消息,handler详解
    Storm流处理项目案例
    021 使用join()将数组转变为字符串
    020 $.each的使用
  • 原文地址:https://www.cnblogs.com/onioncyc/p/8449547.html
Copyright © 2020-2023  润新知