对于这个题,一开始想要解决的就是怎么将向下取整优化掉,否则线段树修改没有意义。其实不然,线段树维护b区间,对于每次a区间+1操作,改变为-1操作,对于这个区间只需求出贡献点,贡献多少即可。用一个sum维护。对于线段树每次暴力修改到每个点,时间复杂度1e8左右。
#include <bits/stdc++.h>
using namespace std;
const int M = 1e5 + 7;
int n, m;
int a[M], b[M], minn[M * 4], lazy[M * 4];
#define ll long long
ll sum[M*4];//sum即区间贡献和
void push_up(int x)
{
minn[x]=min(minn[x*2],minn[x*2+1]);
sum[x]=sum[x*2]+sum[x*2+1];
}
void push_down(int x)
{
if(lazy[x])
{
minn[x*2]-=lazy[x];
minn[x*2+1]-=lazy[x];
lazy[x*2]+=lazy[x];
lazy[x*2+1]+=lazy[x];
lazy[x]=0;
}
}
void build(int i,int l,int r)
{
lazy[i]=0;
if(l==r)
{
minn[i]=b[l];
sum[i]=0;
return;
}
int mid=(l+r)/2;
build(i*2,l,mid);
build(i*2+1,mid+1,r);
push_up(i);
return;
}
void update(int i,int l,int r,int x,int y)
{
if(x<=l&&y>=r)
{
lazy[i]+=1;
minn[i]-=1;
return;
}
push_down(i);
int mid=l+r>>1;
if(x<=mid)
{
update(i*2,l,mid,x,y);
}
if(y>mid)
{
update(i*2+1,mid+1,r,x,y);
}
push_up(i);
}
void change(int i,int l,int r)
{
if(minn[i]>0) return;
if(l==r)
{
int t=b[l]-minn[i];
sum[i]+=t/b[l];
minn[i]=b[l]-t%b[l];//对于minn,因为已经将区间中对于能够贡献给sum的值,暴力修改给sum
return;
}
int mid=l+r>>1;
push_down(i);
change(i*2,l,mid);
change(i*2+1,mid+1,r);
push_up(i);
}
ll query(int i,int l,int r,int x,int y)
{
if(x<=l&&y>=r)
{
if(minn[i]<=0)
{
change(i,l,r);
}
return sum[i];
}
ll ans=0;
int mid=l+r>>1;
push_down(i);
if(x<=mid)
ans+=query(i*2,l,mid,x,y);
if(y>mid)
ans+=query(i*2+1,mid+1,r,x,y);
return ans;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
scanf("%d", &b[i]);
build(1, 1, n);
while (m--) {
int op,l,r;
scanf("%d%d%d", &op, &l, &r);
if (op == 1) {
update(1,1,n,l,r);
}
else {
printf("%lld\n", query(1, 1, n,l,r));
}
}
}