zoukankan      html  css  js  c++  java
  • BZOJ4293: [PA2015]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]<=10^12,0<=b[i]<=10^12),依次描述每次收割。
    数据保证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。
     
    不难发现无论何时草高度从小到大的顺序是不变的,我们可以按生长速度先排序一下。
    然后问题就比较简单了,一次收割其实类似一个区间覆盖,我们只需找到第一个被收割的草即可。
    用线段树维护区间最后一棵草上次被收割的时间、上次剩多长、所有草高度和减去时间*生长高度之和,很容易就O(logn)了。
    #include<cstdio>
    #include<cctype>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define dwn(i,s,t) for(int i=s;i>=t;i--)
    #define ren for(int i=first[x];i;i=next[i])
    using namespace std;
    const int BufferSize=1<<16;
    char buffer[BufferSize],*head,*tail;
    inline char Getchar() {
    	if(head==tail) {
    		int l=fread(buffer,1,BufferSize,stdin);
    		tail=(head=buffer)+l;
    	}
    	return *head++;
    }
    typedef long long ll;
    inline ll read() {
        ll x=0,f=1;char c=Getchar();
        for(;!isdigit(c);c=Getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=Getchar()) x=x*10+c-'0';
        return x*f;
    }
    const int maxn=500010;
    int n,m,A[maxn];
    ll setv[maxn<<2],sett[maxn<<2],suma[maxn<<2],sumv[maxn<<2],maxv[maxn<<2],maxt[maxn<<2];
    void maintain(int o,int l,int r) {
    	int lc=o<<1,rc=lc|1;
    	if(setv[o]>=0) {
    		sumv[o]=setv[o]*(r-l+1)-sett[o]*suma[o];
    		maxv[o]=setv[o];maxt[o]=sett[o];
    	}
    	else if(l<r) {
    		sumv[o]=sumv[lc]+sumv[rc];
    		maxv[o]=maxv[rc];maxt[o]=maxt[rc];
    	}
    }
    void pushdown(int o,int l,int r) {
    	int lc=o<<1,rc=lc|1,mid=l+r>>1;
    	if(setv[o]>=0) {
    		setv[lc]=setv[rc]=setv[o];
    		sett[lc]=sett[rc]=sett[o];
    		maxt[lc]=maxt[rc]=sett[o];
    		sumv[lc]=setv[o]*(mid-l+1)-sett[o]*suma[lc];
    		sumv[rc]=setv[o]*(r-mid)-sett[o]*suma[rc];
    		maxv[lc]=maxv[rc]=setv[o];
    		setv[o]=sett[o]=-1;
    	}
    }
    void build(int o,int l,int r) {
    	if(l==r) suma[o]=A[l];
    	else {
    		setv[o]=-1;
    		int mid=l+r>>1,lc=o<<1,rc=lc|1;
    		build(lc,l,mid);build(rc,mid+1,r);
    		suma[o]=suma[lc]+suma[rc];
    	}
    }
    ll ans,d,b;
    int findst(int o,int l,int r) {
    	if(l==r) return l;
    	else {
    		pushdown(o,l,r);
    		int mid=l+r>>1,lc=o<<1,rc=lc|1;
    		if(maxv[lc]+(d-maxt[lc])*A[mid]>b) return findst(lc,l,mid);
    		return findst(rc,mid+1,r);
    	}
    }
    void update(int o,int l,int r,int ql,int qr) {
    	if(ql<=l&&r<=qr) {
    		ans+=sumv[o]+d*suma[o]-b*(r-l+1);
    		setv[o]=b;sett[o]=d;
    	}
    	else {
    		pushdown(o,l,r);
    		int mid=l+r>>1,lc=o<<1,rc=lc|1;
    		if(ql<=mid) update(lc,l,mid,ql,qr);
    		if(qr>mid) update(rc,mid+1,r,ql,qr);
    	}
    	maintain(o,l,r);
    }
    int main() {
    	n=read();m=read();
    	rep(i,1,n) A[i]=read();
    	sort(A+1,A+n+1);
    	build(1,1,n);
    	rep(i,1,m) {
    		d=read();b=read();
    		if(maxv[1]+(d-maxt[1])*A[n]<=b) puts("0");
    		else {
    			int s=findst(1,1,n);ans=0;
    			update(1,1,n,s,n);printf("%lld
    ",ans);
    		}
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    (QR14)带权的DAG节点排序
    数字组合
    最长连续不重复子序列
    树状数组
    归并排序
    差分
    前缀和
    64位整数乘法
    MySQL8 常用指令
    离线及实时实操架构
  • 原文地址:https://www.cnblogs.com/wzj-is-a-juruo/p/5585293.html
Copyright © 2011-2022 走看看