题目:http://acm.hdu.edu.cn/showproblem.php?pid=6315
题意:
给定一个序列b[i];
数列a[i]初始全0;
每次操作可以向a中[l,r]区间+1;
每次求
分析:
首先将线段树中第i位赋值为b[i],代表i位需要加b[i]次才会对答案+1;
每次区间加操作,认为是对区间-1;
用线段树记录区间最小值及最小值出现的位置;
若区间最小值为0,将这个位置重新赋值为b[i],并在树状数组中对答案+1;
统计答案在树状数组中求和。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int tmax=1e5+5;
int n,ql,qr,b[tmax];
ll v,addv[4*tmax],minv[4*tmax],minnum[4*tmax];
ll _min[2],c[2*tmax];
char s[15];
void query(int x,int l,int r,ll addition)
{
if(ql<=l&&r<=qr)
{
if(_min[0]>minv[x]+addition)
{
_min[0]=minv[x]+addition;
_min[1]=minnum[x];
}
}
else {
int mid=l+(r-l)/2;
if(mid>=ql) query(x*2,l,mid,addition+addv[x]);
if(mid<qr) query(x*2+1,mid+1,r,addition+addv[x]);
}
return;
}
void maintain(int x,int l,int r)
{
int lc=x*2,rc=x*2+1;
minv[x]=0;
if(r>l)
{
if(minv[lc]<=minv[rc])
{
minv[x]=minv[lc];
minnum[x]=minnum[lc];
}
else{
minv[x]=minv[rc];
minnum[x]=minnum[rc];
}
}
else minnum[x]=l;
minv[x]+=addv[x];
return;
}
void update(int x,int l,int r)
{
if(ql<=l&&r<=qr) addv[x]+=v;
else{
int mid=l+(r-l)/2;
if(mid>=ql) update(x*2,l,mid);
if(mid<qr) update(x*2+1,mid+1,r);
}
maintain(x,l,r);
return;
}
int lowbit(int x)
{
return x&-x;
}
ll sum(int x)
{
ll ret=0;
while(x>0)
{
ret+=c[x];
x-=lowbit(x);
}
return ret;
}
void add(int x)
{
while(x<=n)
{
c[x]++;
x+=lowbit(x);
}
return;
}
int main()
{
int i,q,tl,tr;
while(scanf("%d%d",&n,&q)==2)
{
memset(addv,0,sizeof(addv));
memset(minv,0,sizeof(minv));
memset(c,0,sizeof(c));
memset(minnum,0,sizeof(minnum));
for(i=1;i<=n;i++)
{
scanf("%d",&b[i]);
ql=qr=i;
v=b[i];
update(1,1,n);
}
for(i=1;i<=q;i++)
{
scanf("%s%d%d",s,&ql,&qr);
if(s[0]=='q')
printf("%I64d\n",sum(qr)-sum(ql-1));
else{
v=-1;
update(1,1,n);
_min[0]=tmax;
query(1,1,n,0);
tl=ql;tr=qr;
while(_min[0]==0)
{
ql=qr=_min[1];
v=b[ql];
update(1,1,n);
add(ql);
ql=tl;qr=tr;
_min[0]=tmax;
query(1,1,n,0);
}
}
}
}
return 0;
}