【PA 2015】Siano

【题目】

题目描述:

农夫 Byteasar 买了一片 n n n 亩的土地,他要在这上面种草。

他在每一亩土地上都种植了一种独一无二的草,其中,第 i i i 亩土地的草每天会长高 a i a_i ai 厘米。

Byteasar 一共会进行 m m m 次收割,其中第 i i i 次收割在第 d i d_i di 天,并把所有高度大于等于 b i b_i bi 的部分全部割去。Byteasar 想知道,每次收割得到的草的高度总和是多少,你能帮帮他吗?

输入格式:

第一行包含两个正整数 n , m ( 1 ≤ n , m ≤ 500000 ) n,m(1≤n,m≤500000) n,m(1n,m500000),分别表示亩数和收割次数。

第二行包含 n n n 个正整数,其中第 i i i 个数为 a i ( 1 ≤ a i ≤ 1000000 ) a_i(1≤a_i≤1000000) ai(1ai1000000),依次表示每亩种植的草的生长能力。

接下来 m m m 行,每行包含两个正整数 d i , b i ( 1 ≤ d i ≤ 1 0 12 , 0 ≤ b i ≤ 1 0 12 ) d_i,b_i(1≤d_i≤10^{12},0≤b_i≤10^{12}) di,bi(1di10120bi1012),依次描述每次收割。

数据保证 d 1 &lt; d 2 &lt; . . . &lt; d m d_1&lt;d_2&lt;...&lt;d_m d1<d2<...<dm,并且任何时刻没有任何一亩草的高度超过 1 0 12 10^{12} 1012

输出格式:

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

样例数据:

输入
4 4
1 2 4 3
1 1
2 2
3 0
4 4

输出
6
6
18
0

提示:

1 1 1 天,草的高度分别为 1 , 2 , 4 , 3 1,2,4,3 1,2,4,3,收割后变为 1 , 1 , 1 , 1 1,1,1,1 1,1,1,1
2 2 2 天,草的高度分别为 2 , 3 , 5 , 4 2,3,5,4 2,3,5,4,收割后变为 2 , 2 , 2 , 2 2,2,2,2 2,2,2,2
3 3 3 天,草的高度分别为 3 , 4 , 6 , 5 3,4,6,5 3,4,6,5,收割后变为 0 , 0 , 0 , 0 0,0,0,0 0,0,0,0
4 4 4 天,草的高度分别为 1 , 2 , 4 , 3 1,2,4,3 1,2,4,3,收割后变为 1 , 2 , 4 , 3 1,2,4,3 1,2,4,3


【分析】

首先,我们要知道一个东西,即若 a i &gt; a j a_i&gt;a_j ai>aj,那么在任意时间, h i h_i hi 是始终大于 h j h_j hj

那么我们不妨对数组 a a a 进行排序,排序后的草的高度在任意时间内是单调不减的

那么对于每次的询问 d i d_i di,所有草都长了 d i − d i − 1 d_i-d_{i-1} didi1 的时间,这个可以用预处理出前缀和,然后用区间加搞定,而对于 b i b_i bi,由于此时高度一定是单调不减的,可以二分找出第一个要割的位置(在线段树上找),然后统计答案后用区间覆盖

所以这道题就转变成了区间加区间覆盖的简单问题(由于区间和只用返回 s u m r o o t sum_{root} sumroot,连区间和都不用写)


【代码】

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 500005
#define ll long long
using namespace std;
int a[N];
ll d[N],b[N],s[N],sum[N<<2],Max[N<<2],add[N<<2],mark[N<<2];
void Pushup(int root)
{
	sum[root]=sum[root<<1]+sum[root<<1|1];
	Max[root]=max(Max[root<<1],Max[root<<1|1]);
}
void Cover(int root,int l,int r,ll val)
{
	Max[root]=val;
	sum[root]=val*(r-l+1);
	add[root]=0,mark[root]=val;
}
void Add(int root,int l,int r,ll val)
{
	add[root]+=val;
	Max[root]+=val*a[r];
	sum[root]+=val*(s[r]-s[l-1]);
}
void Pushdown(int root,int l,int r,int mid)
{
	if(mark[root]!=-1)  Cover(root<<1,l,mid,mark[root]),Cover(root<<1|1,mid+1,r,mark[root]);
	if(add[root])  Add(root<<1,l,mid,add[root]),Add(root<<1|1,mid+1,r,add[root]);
	add[root]=0,mark[root]=-1;
}
int find(int root,int l,int r,ll x)
{
	if(l==r)return l;
	int mid=(l+r)>>1;
	Pushdown(root,l,r,mid);
	if(x<=Max[root<<1])  return find(root<<1,l,mid,x);
	return find(root<<1|1,mid+1,r,x);
}
void Modify(int root,int l,int r,int x,int y,ll val)
{
	if(l>=x&&r<=y)
	{
		Cover(root,l,r,val);
		return;
	}
	int mid=(l+r)>>1;
	Pushdown(root,l,r,mid);
	if(x<=mid)  Modify(root<<1,l,mid,x,y,val);
	if(y>mid)  Modify(root<<1|1,mid+1,r,x,y,val);
	Pushup(root);
}
int main()
{
	int n,m,i;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;++i)
	  scanf("%d",&a[i]);
	sort(a+1,a+n+1);
	for(i=1;i<=n;++i)
	  s[i]=s[i-1]+a[i];
	for(i=1;i<=m;++i)
	{
		scanf("%lld%lld",&d[i],&b[i]);
		Add(1,1,n,d[i]-d[i-1]);ll ans=sum[1];
		if(Max[1]<=b[i]){printf("0\n");continue;}
		int pos=find(1,1,n,b[i]);
		Modify(1,1,n,pos,n,b[i]);
		printf("%lld\n",ans-sum[1]);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值