1022 - 线段树 - Siano[PA2015](BZOJ 4293)

[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个数为ai,依次表示每亩种植的草的生长能力。 接下来m行,每行包含两个正整数d[i],bi,依次描述每次收割。 数据保证d[1]<d[2]<…<d[m],并且任何时刻没有任何一亩草的高度超过10^12。

输出
Output

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

样例输入 [复制]
4 4

1 2 4 3

1 1

2 2

3 0

4 4
样例输出 [复制]
6

6

18

0
提示
第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

标签
BZOJ4293 电子科大春季营考试最后一题

分析

令人窒息……刚刚全部都认认真真地写了题解,然后电脑莫名重启,csdn居然没有帮我保存……啊啊啊,差评
算了,再来一遍
用线段树搞:
1.长草变成区间加
2.查询区间内刚好小于等于某个数的下标
3.大于割草高度的全部区间覆盖成割草高度,然后比较覆盖前后的区间和就行了。

要注意长草的时候,lazy(加标记)维护的是生长的天数,所以区间和的话应该是这个天数乘以相应的生长高度(这个高度,可以用前缀和预处理,表现在代码中就是 t o t tot tot数组)

原谅水了一波,电脑三次重启,实在令人窒息,没心情写了

代码
#include<bits/stdc++.h>
#define in read()
#define lc (k<<1)
#define rc (k<<1)|1
#define N 500009
#define int long long
#define ll long long
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9'){
		res=(res<<3)+(res<<1)+ch-'0';
		ch=getchar();
	}
	return f==1?res:-res;
}
int n,m,a[N];
ll maxn[N<<2],sum[N<<2],lzy[N<<2],tag[N<<2];
ll tot[N];
void Cover(int k,int l,int r,ll v){
	lzy[k]=0;
	tag[k]=maxn[k]=v;
	sum[k]=(r-l+1)*v;
}
void Add(int k,int l,int r,ll v){
	lzy[k]+=v;
	maxn[k]+=v*a[r];sum[k]+=(tot[r]-tot[l-1])*v;
}
void pushdown(int k,int l,int r){
	int mid=l+r>>1;
	if(tag[k]!=-1) Cover(lc,l,mid,tag[k]),Cover(rc,mid+1,r,tag[k]),tag[k]=-1;
	if(lzy[k]) Add(lc,l,mid,lzy[k]),Add(rc,mid+1,r,lzy[k]),lzy[k]=0;
}
int find(int k,int l,int r,ll x){
	if(l==r) return l;
	pushdown(k,l,r);
	int mid=l+r>>1;
	if(maxn[lc]>=x) return find(lc,l,mid,x);/
	else return find(rc,mid+1,r,x);
}
void pushup(int k){
	sum[k]=sum[lc]+sum[rc];
	maxn[k]=max(maxn[lc],maxn[rc]);
}
void modify(int k,int l,int r,int x,int y,ll v){
	if(x<=l&&r<=y){
		Cover(k,l,r,v);return;
	}
	pushdown(k,l,r);
	int mid=l+r>>1;
	if(x<=mid) modify(lc,l,mid,x,y,v);
	if(y>mid) modify(rc,mid+1,r,x,y,v);
	pushup(k);
}
signed main(){
	n=in;m=in;
	int i,j,k;
	memset(tag,-1,sizeof(tag));
	tot[0]=0;
	for(i=1;i<=n;++i) a[i]=in;
	sort(a+1,a+n+1);
	for(i=1;i<=n;++i) tot[i]=tot[i-1]+a[i];
	int d1=0,d2;
	ll b;
	for(i=1;i<=m;++i){
		d2=in;scanf("%lld",&b);
		lzy[1]+=1ll*(d2-d1);sum[1]+=1ll*(d2-d1)*tot[n];maxn[1]+=1ll*a[n]*(d2-d1);
		if(maxn[1]<=b){printf("0\n");d1=d2;continue;	}
		ll ans=sum[1];
		int pos=find(1,1,n,b);
		modify(1,1,n,pos,n,b);//qujian fuzhi
		cout<<ans-sum[1]<<'\n';
		d1=d2;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值