题意
农夫约翰打算重修他的农场。他有 N 块土地,连续排列成一行,标号为 1…N。在每块土地上有任意数量的草堆。他可以发出三种指令:
1) 对一个连续区间的土地,每块土地增加相同数量的草堆。
2) 对一个连续区间的土地,输出其中最少的草堆数量。
3) 对一个连续区间的土地,输出草堆数量总数。
第一行两个正整数,N (1≤N≤200,000) 和 Q (1≤Q≤100,000)。
下一行是N个非负整数,最大100,000,表示每块土地上有多少个草堆。
以下Q行,每行一单个大写字母开头(M,P或S),空格后跟随两个正整数 A 和 B (1≤A≤B≤N), 或者三个正整数 A, B, 和 C (1≤A≤B≤N; 1≤C≤100,000)。当且仅当第一个字母是 P 时,是三个正整数。
当该字母是M,输出区间A…B的最小草堆数。
当该字母是P,在区间A…B,每块土地增加C堆草。
当该字母是M,输出区间A…B的草堆数之和。
每行一个数字,用于响应’M’ 或 ‘S’ 命令。
样例
SAMPLE INPUT:
4 5
3 1 2 4
M 3 4
S 1 3
P 2 3 1
M 3 4
S 1 3
SAMPLE OUTPUT:
2
6
3
8
分析
一道线段树的裸题,维护区间的最小值、和。
没什么好说的,直接套用线段树。
注意用long long
代码
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
#include<iostream>
#include<cmath>
#include<queue>
#define ll long long
using namespace std;
const int inf=2000000001;
const int maxn=200000+20;
const int manq=100000+20;
ll n,q;
struct Tree
{
ll y1,y2,v;
ll sumv[maxn*4];
ll minv[maxn*4];
ll addv[maxn*4];
void maintain(ll o,ll L,ll R)
{
ll lc=o*2,rc=o*2+1;
sumv[o]=minv[o]=0;
if(R>L){
sumv[o]=sumv[lc]+sumv[rc];
minv[o]=min(minv[lc],minv[rc]);
}
minv[o]+=addv[o];sumv[o]+=addv[o]*(R-L+1);
}
void update(ll o,ll L,ll R){
ll lc=o*2;
ll rc=o*2+1;
if(y1<=L&&y2>=R)
addv[o]+=v;
else{
ll M=(L+R)>>1;
if(y1<=M) update(lc,L,M);
if(y2>M) update(rc,M+1,R);
}
maintain(o,L,R);
}
ll _min,_sum;
void query(ll o,ll L,ll R,ll add){
if(y1<=L&&y2>=R){
_sum+=sumv[o]+add*(R-L+1);
_min=min(_min,minv[o]+add);
}
else{
ll M=(R+L)>>1;
if(y1<=M) query(o*2,L,M,add+addv[o]);
if(y2>M) query(o*2+1,M+1,R,add+addv[o]);
}
}
}t;
int main()
{
//freopen("haybales.in","r",stdin);
//freopen("haybales.out","w",stdout);
cin>>n>>q;
for(ll i=1;i<=n;i++)
{
scanf("%d",&t.v);
t.y2=t.y1=i;
t.update(1,1,n);
}
char a[5];
while(q--)
{
scanf("%s",a);
if(a[0]=='M'){
t._min=inf;
scanf("%d%d",&t.y1,&t.y2);
t.query(1,1,n,0);
cout<<t._min;
printf("\n");
}
else if(a[0]=='S'){
t._sum=0;
scanf("%d%d",&t.y1,&t.y2);
t.query(1,1,n,0);
cout<<t._sum;
printf("\n");
}
else if(a[0]=='P'){
scanf("%d%d%d",&t.y1,&t.y2,&t.v);
t.update(1,1,n);
}
}
return 0;
}