背景:
水题都要找半天错。
题目传送门:
https://www.luogu.org/problem/P4879
题意:
基本操作就是,在第
x
x
x位置改值;将第
o
p
op
op个有值的位置的值删除;询问整段的和。
思路:
除了删除,树状数组都可以优秀的解决。
考虑记录当前这个位置是否有值,在对这个统计一个前缀和,那么找第
o
p
op
op个位置就可以二分了。
理论时间复杂度:
Θ
(
n
log
2
长
度
)
\Theta(n\log^2 长度)
Θ(nlog2长度)。
当然你用线段树在树中二分地去找也行,时间复杂度:
Θ
(
n
log
长
度
)
\Theta(n\log 长度)
Θ(nlog长度)
然而树状数组不光代码优秀,而且实测飞快,下面的代码
169ms
\text{169ms}
169ms(没有卡常),基本碾压大多数线段树。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define lowbit(x) ((x)&(-(x)))
using namespace std;
int n,m;
LL a[500010],tr1[500010],tr2[500010];
void add(LL *tr,int x,LL y)
{
for(;x<=500000;x+=lowbit(x))
tr[x]+=y;
}
LL getsum(LL *tr,int x)
{
LL sum=0;
for(;x;x-=lowbit(x))
sum+=tr[x];
return sum;
}
int find(int x)
{
int l=1,r=500000,mid,op;
while(l<=r)
{
mid=(l+r)>>1;
int tmp=getsum(tr1,mid);
if(x<=tmp) op=mid,r=mid-1; else l=mid+1;
}
return op;
}
int main()
{
int x;
LL y;
char s[5];
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
a[i]=x;
add(tr1,i,1),add(tr2,i,x);
}
for(int i=1;i<=m;i++)
{
scanf("%s",s+1);
if(s[1]=='C')
{
scanf("%d %lld",&x,&y);
add(tr2,x,-y);
a[x]-=y;
}
else if(s[1]=='I')
{
scanf("%d %lld",&x,&y);
if(!(getsum(tr1,x)-getsum(tr1,x-1))) add(tr1,x,1); else add(tr2,x,-a[x]);
a[x]=y,add(tr2,x,a[x]);
}
else if(s[1]=='D')
{
scanf("%d",&x);
int tmp=find(x);
if(tmp!=-1) add(tr1,tmp,-1),add(tr2,tmp,-a[tmp]);
}
else if(s[1]=='Q')
{
printf("%lld\n",getsum(tr2,500000));
}
}
}