题解 P2801 【教主的魔法】
这其实是一个线段树的裸题,就是要改一下query函数
1.先打一个支持区间修改,查询区间最小值的的普通线段树
2把if(L<=l&&r<=R)return a[cur];改成下面这两句 .
if(L<=l&&r<=R&&a[cur]>=num)return r-l+1;
if(l==r)return a[cur]>=num;
作用:如果本区间最小值比num大,直接把整个区间长度输出,否则继续执行二分(分成了一个点的时候当然要停下)
比普通的分块肯定是要快不少 O2 172MS
#include<iostream>
#include<cstdio>
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-')f=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
int n,m,a[4000001],lazy[4000001],k[1000001],x,y,z;char c;
void buildtree(int l,int r,int cur)
{
if(l==r)
{
a[cur]=k[l];
return ;
}
int mid=(l+r)>>1;
buildtree(l,mid,cur<<1);
buildtree(mid+1,r,cur<<1|1);
a[cur]=min(a[cur<<1],a[cur<<1|1]);
}
void pushdown(int cur)
{
if(lazy[cur]==0)return;
lazy[cur<<1]+=lazy[cur];
lazy[cur<<1|1]+=lazy[cur];
a[cur<<1]+=lazy[cur];
a[cur<<1|1]+=lazy[cur];
lazy[cur]=0;
}
void updata(int l,int r,int L,int R,int cost,int cur)
{
if(L<=l&&r<=R)
{
a[cur]+=cost;
lazy[cur]+=cost;
return ;
}
int mid=(l+r)>>1;
pushdown(cur);
if(L<=mid)updata(l,mid,L,R,cost,cur<<1);
if(R>=mid+1)updata(mid+1,r,L,R,cost,cur<<1|1);
a[cur]=min(a[cur<<1],a[cur<<1|1]);
}
int query(int l,int r,int L,int R,int num,int cur)
{
if(L<=l&&r<=R&&a[cur]>=num)return r-l+1;
if(l==r)return a[cur]>=num;
int mid=(l+r)>>1,ans=0;
pushdown(cur);
if(L<=mid)ans+=query(l,mid,L,R,num,cur<<1);
if(R>=mid+1)ans+=query(mid+1,r,L,R,num,cur<<1|1);
return ans;
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;i++)k[i]=read();
buildtree(1,n,1);
for(int i=1;i<=m;i++)
{
cin>>c;x=read();y=read();z=read();
if(c=='M')updata(1,n,x,y,z,1);
if(c=='A')printf("%d\n",query(1,n,x,y,z,1));
}
system("pause");
return 0;
}