题目链接:P4879 ycz的妹子
操作很多,但都是吓人的。
一眼线段树,我不会告诉你我是搜线段树标签找到这题的。
考虑用每个节点来维护每个城市中妹子的情况,显然只需维护:妹子的颜值与此城市是否有热恋中的妹子。
开始想维护两个线段树,经过冷静后发现在一棵线段树上搞即可。
看数据范围,知道要注意(long;long)。
来看基本操作(应该都是小儿科的了):
(1).更新及建树:
void update(int k){a[k].sum=a[k<<1].sum+a[k<<1|1].sum,a[k].cnt=a[k<<1].cnt+a[k<<1|1].cnt;}
void build(int k,int l,int r,int x)
{
a[k].l=l,a[k].r=r;
if(l==r)
{
if(l<=x) a[k].cnt++;
a[k].sum=(ll)t[l];
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid,x),build(k<<1|1,mid+1,r,x);
update(k);
}
(2).把一个城市妹子颜值降低:
void turn(int k,int x,int y)
{
if(a[k].l==a[k].r){a[k].sum-=(ll)y;return;}
int mid=(a[k].l+a[k].r)>>1;
if(x<=mid) turn(k<<1,x,y);
else turn(k<<1|1,x,y);
update(k);
}
(3).在一个城市上加一个妹子,注意把有妹子的标记打上:
void turn_to(int k,int x,int y)
{
if(a[k].l==a[k].r){a[k].sum=(ll)y,a[k].cnt=1;return;}
int mid=(a[k].l+a[k].r)>>1;
if(x<=mid) turn_to(k<<1,x,y);
else turn_to(k<<1|1,x,y);
update(k);
}
(4).寻找第(x)个有妹子的城市:
这时候就要用到有无妹子的计数器了,我们在线段树上每次判断是在左儿子还是在右儿子即可。和权值线段树确定第(k)大(小)数的思想是一样的。
int find(int k,int x)
{
if(a[k].l==a[k].r){return a[k].l;}
int mid=a[k<<1].cnt;
if(x<=mid) return find(k<<1,x);
else return find(k<<1|1,x-mid);
}
(5).把在某个城市的妹子踢掉。
这里的函数名十分生动:(kick)......
我们把颜值设成(0),并把标记打成“无妹子”即可。
void kick(int k,int x)
{
if(a[k].l==a[k].r){a[k].cnt=a[k].sum=(ll)0;return;}
int mid=(a[k].l+a[k].r)>>1;
if(x<=mid) kick(k<<1,x);
else kick(k<<1|1,x);
update(k);
}
(6).颜值和:
由于是所有数的和,只需输出树根的总颜值即可。
需要注意的是:对于所有修改操作,记得不断更新((update))。
在这里提供一个拓展题:把查询总颜值改为每次求可以得到妹子的最大颜值,这样的难度就有了提高,大家可以想一下做法。
下面给出完整代码:
(Code):
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define MAXN 500005
#define ll long long
int n,m,x,y,t[MAXN];
char c;
struct node
{
int l,r,cnt;
ll sum;
node(){sum=cnt=(ll)0;}
}a[MAXN<<2];
void update(int k){a[k].sum=a[k<<1].sum+a[k<<1|1].sum,a[k].cnt=a[k<<1].cnt+a[k<<1|1].cnt;}
void build(int k,int l,int r,int x)
{
a[k].l=l,a[k].r=r;
if(l==r)
{
if(l<=x) a[k].cnt++;
a[k].sum=(ll)t[l];
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid,x),build(k<<1|1,mid+1,r,x);
update(k);
}
void turn(int k,int x,int y)
{
if(a[k].l==a[k].r){a[k].sum-=(ll)y;return;}
int mid=(a[k].l+a[k].r)>>1;
if(x<=mid) turn(k<<1,x,y);
else turn(k<<1|1,x,y);
update(k);
}
void turn_to(int k,int x,int y)
{
if(a[k].l==a[k].r){a[k].sum=(ll)y,a[k].cnt=1;return;}
int mid=(a[k].l+a[k].r)>>1;
if(x<=mid) turn_to(k<<1,x,y);
else turn_to(k<<1|1,x,y);
update(k);
}
int find(int k,int x)
{
if(a[k].l==a[k].r){return a[k].l;}
int mid=a[k<<1].cnt;
if(x<=mid) return find(k<<1,x);
else return find(k<<1|1,x-mid);
}
void kick(int k,int x)
{
if(a[k].l==a[k].r){a[k].cnt=a[k].sum=(ll)0;return;}
int mid=(a[k].l+a[k].r)>>1;
if(x<=mid) kick(k<<1,x);
else kick(k<<1|1,x);
update(k);
}
int main()
{
#define read(x) scanf("%d",&x)
read(n),read(m);
for(int i=1;i<=m;i++) read(t[i]);
build(1,1,500000,n);
for(int i=1;i<=m;i++)
{
cin>>c;
if(c=='C') read(x),read(y),turn(1,x,y);
else if(c=='I') read(x),read(y),turn_to(1,x,y);
else if(c=='D') read(x),y=find(1,x),kick(1,y);
else printf("%lld
",a[1].sum);
}
return 0;
}