(该做法可能不是最优做法,见谅)
题目大意:有若干个城市(最多5*10^5个),一开始1~n城市有妹子,每个妹子有一个颜值,有四个操作。I x y表示将城市x的妹子的颜值改成y(没有妹子就当出现了个新的),C x y表示第x个城市的妹子的颜值-y,Q表示询问现有的妹子的颜值总和,D x表示从左往右数第x个妹子没了。
那么根据直觉,我们首先考虑优雅的做法——暴力。我们发现,前三个操作都可以暴力O(1)完成,于是无脑的打出以下代码:
char s[5];
int x,y;
scanf("%s",s);
if(s[0]=='C')
{
scanf("%d %d",&x,&y);
a[x]-=(ll)y;sum-=(ll)y;
}
if(s[0]=='I')
{
scanf("%d %d",&x,&y);
if(v[x])sum-=(ll)a[x];//v表示该城市是否有妹子,a记录该妹子的颜值
sum+=(ll)(a[x]=y);
v[x]=true;
}
if(s[0]=='Q')printf("%lld\n",sum);
好的这题我们已经做完一半了。
那么接下来考虑一下删除操作。(我才不会告诉你如果暴力搞删除操作再吸个氧其实就可以AC)
……
啊呀不用暴力有点难搞啊。。然后果断点开算法标签 经过我一番深思熟虑,找到了一种搞法:假如我们用一个数组l记录每个城市是否有妹子,su[i]=1表示有,=0表示没有,然后统计一下他的前缀和,可以发现大概长这个样子:
0 0 1 1 1 1 2 3 4 4 5 ……
再仔细一想,每一个数字第一次出现的位置其实就是那个妹子所在的位置呀!比如:1第一次出现是在3的位置,于是可以得到,第一个妹子在3号城市。
然后发现每次新出现一个妹子就只需要将(sum表示su的前缀和)sum[x~maxn](妹子所在的城市 到 编号最大的城市)+1即可,分手时同理。于是可以用线段树维护
然后这题就做完了。
代码如下,细节在代码里面会讲(首次使用指针写代码,写的不好请见谅,上文中的su和sum数组在代码中其实用不到,所以并没有写入代码,以及代码中的sum的定义和上面的不一样):
#include <cstdio>
#include <cstring>
#define ll long long
#define maxn 500000
ll a[maxn+10];
bool v[maxn+10];
int n,m;
ll sum=0;
struct node{
int l,r,le,ri;//l、r记录管理的城市范围,le、ri记录l城市和r城市的前缀和
node *zuo,*you;
ll late;//大家可能习惯用lazy吧。。
node(){zuo=you=NULL;late=0;}
void bt(int x,int y)//建树
{
l=x;r=y;
int mid=x+y>>1;
if(x==y){le=ri=l>n?n:l;return;}
zuo=new node;
you=new node;
zuo->bt(x,mid);
you->bt(mid+1,y);
le=zuo->le;ri=you->ri;
}
void give()//将late分配下去
{
if(late==0)return;
le+=late;ri+=late;
if(zuo!=NULL)zuo->late+=late,you->late+=late;
late=0;
}
int find(int x)//查找x妹子出现的位置,也就是查找前缀和中数字x第一次出现的位置
{
if(l==r)return l;
zuo->give();
if(zuo->le<=x&&x<=zuo->ri)return zuo->find(x);//如果要找的数字在左儿子管理的城市范围内
else return you->give(),you->find(x);
}
void change(int x,int y,int z)
{
if(l==x&&r==y){late+=z;return;}
int mid=l+r>>1;
if(y<=mid)zuo->change(x,y,z);
else if(x>=mid+1)you->change(x,y,z);
else zuo->change(x,mid,z),you->change(mid+1,y,z);
if(x==l)le+=z;if(r==y)ri+=z;//记得修改一下
}
};
node *root=new node;
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]),sum+=(ll)a[i],v[i]=true;
root->bt(1,maxn);
for(int i=1;i<=m;i++)
{
char s[5];
int x,y;
scanf("%s",s);
if(s[0]=='C')
{
scanf("%d %d",&x,&y);
a[x]-=(ll)y;sum-=(ll)y;
}
if(s[0]=='I')
{
scanf("%d %d",&x,&y);
if(v[x])sum-=(ll)a[x],sum+=(ll)(a[x]=y);
else//如果出现了一个新妹子,那么x~maxn的前缀和都要+1
{
a[x]=y;sum+=(ll)y;
root->give();
root->change(x,maxn,1);
}
v[x]=true;
}
if(s[0]=='Q')printf("%lld\n",sum);
if(s[0]=='D')
{
scanf("%d",&x);
root->give();
int p=root->find(x);//找到第x的妹子
sum-=(ll)a[p];a[p]=0;v[p]=false;//删掉她
root->change(p,maxn,-1);//p~maxn前缀和-1
}
}
}