4293: [PA2015]Siano
Time Limit: 30 Sec Memory Limit: 256 MB
Submit: 581 Solved: 199
[Submit][Status][Discuss]
Description
农夫
Byteasar
B
y
t
e
a
s
a
r
买了一片
n
n
亩的土地,他要在这上面种草。
他在每一亩土地上都种植了一种独一无二的草,其中,第亩土地的草每天会长高
ai
a
i
厘米。
Byteasar
B
y
t
e
a
s
a
r
一共会进行
m
m
次收割,其中第次收割在第
di
d
i
天,并把所有高度大于等于
bi
b
i
的部分全部割去。
Byteasar
B
y
t
e
a
s
a
r
想知道,每次收割得到的草的高度总和是多少,你能帮帮他吗?
Input
第一行包含两个正整数
n,m(1≤n,m≤500000)
n
,
m
(
1
≤
n
,
m
≤
500000
)
,分别表示亩数和收割次数。
第二行包含
n
n
个正整数,其中第个数为
ai(1≤ai≤1000000)
a
i
(
1
≤
a
i
≤
1000000
)
,依次表示每亩种植的草的生长能力。
接下来
m
m
行,每行包含两个正整数,依次描述每次收割。
数据保证
d1<d2<...<dm
d
1
<
d
2
<
.
.
.
<
d
m
,并且任何时刻没有任何一亩草的高度超过
1012
10
12
。
Output
输出 m 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。
解:
感觉自己是一个智障,前几天才看过一道类似的题,比赛场上疯狂RE。
今天调读入忘开 long long l o n g l o n g 还调了仨小时。。。
其实挺简单的,按生长速度排序,可以发现在任何时刻草的高度都是按高度从小到大排序。我们每次查询修改就在线段树上二分到第一个大于该割的位置,然后查询答案,区间修改。
二分的写法需要注意,直接在线段树上二分,不要二分的时候在线段树上查询,不然会是 log2 l o g 2 的。
需要注意的就是,下传标记先下传赋值在下传加标记。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct segment_tree{
int lson,rson,l,r;
long long num,lll,tag,plu;//lll记录这个区间最左边数的值
}b[1000005];
int n,m,cnt,root,zui;
long long v[500005],qv[500005],las,d,x;
void build(int &u,int l,int r)
{
u=++cnt;
b[u].l=l,b[u].r=r;
b[u].tag=-1;
if(l==r) return;
int mid=(l+r)/2;
build(b[u].lson,l,mid);
build(b[u].rson,mid+1,r);
}
void update(int u)
{
b[u].num=b[b[u].lson].num+b[b[u].rson].num;
b[u].lll=b[b[u].lson].lll;
}
void pushdown(int u)//先赋值,后区间加
{
if(b[u].tag!=-1)
{
b[b[u].lson].num=(b[b[u].lson].r-b[b[u].lson].l+1)*b[u].tag;
b[b[u].rson].num=(b[b[u].rson].r-b[b[u].rson].l+1)*b[u].tag;
b[b[u].lson].tag=b[u].tag;b[b[u].lson].plu=0;
b[b[u].rson].tag=b[u].tag;b[b[u].rson].plu=0;
b[b[u].lson].lll=b[u].tag;b[b[u].rson].lll=b[u].tag;
b[u].tag=-1;
}
if(b[u].plu!=0)
{
b[b[u].lson].num+=b[u].plu*(qv[b[b[u].lson].r]-qv[b[b[u].lson].l-1]);
b[b[u].rson].num+=b[u].plu*(qv[b[b[u].rson].r]-qv[b[b[u].rson].l-1]);
b[b[u].lson].plu+=b[u].plu;
b[b[u].rson].plu+=b[u].plu;
b[b[u].lson].lll+=v[b[b[u].lson].l]*b[u].plu;
b[b[u].rson].lll+=v[b[b[u].rson].l]*b[u].plu;
b[u].plu=0;
}
}
long long query(int u,long long da,long long x)//区间赋值
{
pushdown(u);
if(b[u].lll+v[b[u].l]*da>x)//如果最左边需要割,这个区间都要割
{
zui=min(zui,b[u].l-1);
b[u].num+=da*(qv[b[u].r]-qv[b[u].l-1]);
long long ret=b[u].num-x*(b[u].r-b[u].l+1);
b[u].num=x*(b[u].r-b[u].l+1);
b[u].tag=x;b[u].lll=x;b[u].plu=0;
return ret;
}
if(b[u].l==b[u].r) return 0;
if(b[b[u].rson].lll+v[b[b[u].rson].l]*da>=x)//如果右儿子最左边需要割,左儿子可能也需要割
return query(b[u].lson,da,x)+query(b[u].rson,da,x);
else return query(b[u].rson,da,x);//右儿子最左边都不割,左儿子肯定不割
update(u);
}
void modify(int u,int l,int r,long long da)//区间加,标记打过了几天
{
pushdown(u);
if(l==b[u].l&&r==b[u].r)
{
b[u].num+=da*(qv[b[u].r]-qv[b[u].l-1]);
b[u].plu+=da;b[u].lll+=da*v[b[u].l];
return;
}
int mid=(b[u].l+b[u].r)/2;
if(r<=mid) modify(b[u].lson,l,r,da);
else if(l>mid) modify(b[u].rson,l,r,da);
else modify(b[u].lson,l,mid,da),modify(b[u].rson,mid+1,r,da);
update(u);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&v[i]);
sort(v+1,v+1+n);
for(int i=1;i<=n;i++)
qv[i]=qv[i-1]+v[i];
build(root,1,n);
for(int i=1;i<=m;i++)
{
zui=n;//zui用于记录区间加在哪里结束
scanf("%lld%lld",&d,&x);//mmp就是这个是longlong,mmp读入就longlong,mmpBZOJ交上去还不知道哪里wa了,mmp对拍了俩小时都没拍出来</span>
printf("%lld\n",query(root,d-las,x));
if(zui!=0) modify(root,1,zui,d-las);
las=d;
}
}