链接
虽然题目很真实,但还是得无情简化
给一个序列,在序列上进行:
1.单点修改
2.求和
3.删除第x个点(将第x个点的位置修改为0)
4.新增一个点(相当于将对应点+y)
只看前两条,完全可以用树状数组/线段树解决。
第三条可以想一下,我们需要在线修改,第x个节点的位置是会随时变的,如何维护呢?
可以记录一下每个区间的节点数,用类似于二分的方法维护。
这样的话,直接选择线段树,考虑加tot就可以了。
注释在代码里
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#define maxn 500005
using namespace std;
int n,m;
char c;
int x,y;
long long a[maxn];
struct T{
int l,r,tot;//tot表示区间内有多少妹子
long long sum;//颜值和
}tr[maxn<<2];
void pushup(int t)
{
tr[t].tot=tr[t<<1].tot+tr[t<<1|1].tot;
tr[t].sum=tr[t<<1].sum+tr[t<<1|1].sum;
}
void biu(int t,int x,int y)
{
tr[t].l=x,tr[t].r=y;
if(x==y){
tr[t].sum=a[x];
if(a[x])++tr[t].tot;//建树时修改tot
return ;
}
int mid=(x+y)>>1;
biu(t<<1,x,mid);
biu(t<<1|1,mid+1,y);
pushup(t);
}
void ad(int t,int p,int z)
{
int l=tr[t].l,r=tr[t].r,mid=(l+r)>>1;
if(l==r){
tr[t].sum-=z;
return ;
}
else if(p<=mid)ad(t<<1,p,z);
else ad(t<<1|1,p,z);
pushup(t);
}//单点修改
void change(int t,int p,int z)
{
int l=tr[t].l,r=tr[t].r,mid=(l+r)>>1;
if(l==r){
tr[t].sum=z;
tr[t].tot=1;//不管之前有没有,现在tot为1就可以了
return ;
}
else if(p<=mid)change(t<<1,p,z);
else change(t<<1|1,p,z);
pushup(t);
}//加点,因为是覆盖关系,所以直接修改
void del(int t,int x)
{
int l=tr[t].l,r=tr[t].r;
if(l==r&&tr[t].tot==x){
--tr[t].tot;
tr[t].sum=0;
return ;
}//正好删到了第x个点
else if(tr[t<<1].tot>=x)del(t<<1,x);//左半部分已经包含了x个
else del(t<<1|1,x-tr[t<<1].tot);//不够,去找右半部分的第(x-tr[t<<1].tot)个
pushup(t);
}//删除
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
biu(1,1,500000);
while(m--){
cin>>c;
if(c=='Q'){
printf("%lld\n",tr[1].sum);
}
if(c=='C'){
scanf("%d%d",&x,&y);
ad(1,x,y);
}
if(c=='I'){
scanf("%d%d",&x,&y);
change(1,x,y);
}
if(c=='D'){
scanf("%d",&x);
del(1,x);
}
}
}
这个题我试过一些很鬼畜的做法,不仅智障程度极高,而且150行+
有一个问题就是删掉求和函数直接输出反而变慢了,真是令人费解啊