Codeforces 85D Sum of Medians

题意:

有n个操作,add x表示向集合添加元素x,del x表示删除集合中的x元素,sum表示求集合所有下标i%5==3的元素之和,注意里的元素都是有序且无重复的。

思路:

线段树+离线处理,单点更新,O(1)查询。为每个节点存储两种信息,一种为当树向上更新时右区间需要平移的个数mov,另一种为ans[i]表示该区间内所有下标%5==i的元素的和。然后树的向上更新操作就为

sum[rt].mov=(sum[rt<<1].mov+sum[rt<<1|1].mov)%5;

for(int i=0;i<5;i++){

sum[rt].ans[i]=sum[rt<<1].ans[i]+sum[rt<<1|1].ans[(i-sum[rt<<1].mov+5)%5];

}

接下来是离线处理问题,即先把问题存在数组中。由于这集合的元素都是有序且无重复的。所以我们可以借助set来离散化元素值,使其按从小到大的顺序对应一个位置,最后才能方便地在树上进行加入、删除操作。

#include<cstdio>
#include<set>
#include<map>
#include<cstring>
using namespace std;
typedef __int64 LL;

const int MAX=1e5+5;

struct Node{
	int mov;
	LL ans[5];
}sum[MAX<<2];

struct Que{
	int id,x;
}Q[MAX];

int n;
set<int> s;
map<int,int> pos;
char op[5];

void PushUp(int rt){
	sum[rt].mov=(sum[rt<<1].mov+sum[rt<<1|1].mov)%5;
	for(int i=0;i<5;i++){
		sum[rt].ans[i]=sum[rt<<1].ans[i]+sum[rt<<1|1].ans[(i-sum[rt<<1].mov+5)%5];
	}
}

void UpDate(int pos,int c,int l,int r,int rt){
	if(l==r){
		if(c>0){
			sum[rt].mov=1;
			sum[rt].ans[1]=c;
		}
		else{
			sum[rt].mov=0;
			sum[rt].ans[1]=0;
		}
		return;
	}
	int mid=(l+r)>>1;
	if(pos<=mid) UpDate(pos,c,l,mid,rt<<1);
	else UpDate(pos,c,mid+1,r,rt<<1|1);
	PushUp(rt);
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%s",op);
		if(op[0]=='a'){
			scanf("%d",&Q[i].x);
			s.insert(Q[i].x);
			Q[i].id=0;
		}
		if(op[0]=='d'){
			scanf("%d",&Q[i].x);
			Q[i].id=1;
		}
		if(op[0]=='s'){
			Q[i].id=2;
		}
	}
	int cnt=1;
	set<int>::iterator it;
	for(it=s.begin();it!=s.end();it++){
		pos[(*it)]=cnt++;
	}
	cnt--;
	memset(sum,0,sizeof(sum));
	for(int i=1;i<=n;i++){
		if(Q[i].id==0){
			UpDate(pos[Q[i].x],Q[i].x,1,cnt,1);
		}
		if(Q[i].id==1){
			UpDate(pos[Q[i].x],-Q[i].x,1,cnt,1);
		}
		if(Q[i].id==2){
			printf("%I64d\n",sum[1].ans[3]);
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值