CF85D Sum of Medians

考虑一个离线做法
暂时先不去考虑这个余3的限制,假设是求排序后前一半数的和,那么很明显,对于每一个add和del,将区间进行加,减修改,最后求一个类似query(1,1,n/2)的值即可。
那么现在是求余3的和,也离线来做吧。
对于add(x),就找到x的位置pos,将[pos,pos]区间累加上x,对于del(x),也是找到pos,将[pos,pos]区间累加上x。那么怎么求哪些数排完序后是余3的呢?
我们把原来的sum[k]表示线段树第k个区间的区间和,变为sum[k][0],sum[k][1],sum[k][2],sum[k][3],sum[k][4],表示线段树第k个区间中,下标余0,余1,余2,余3,余4的区间和。
那么如何实现维护呢?并不是简单的sum[k][i]=sum[k<<1][i]+sum[k<<1|1][i]就可以的了。
正确的公式应该是sum[k][i]=sum[k<<1][i]+sum[k<<1|1][i-now],now是k<<1区间内的数的个数,所以我们再维护一个add[k]表示线段树的第k个区间的区间内有多少个数,这样一来,设t=add[k<<1],那么sum[k][i]=sum[k<<1][i]+sum[k<<1|1][((i-add[k<<1)%5+5)%5]。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+5; 
int n,tot;
int b[N],add[N<<2];
ll sum[N<<2][5];
struct number{int x,f;}num[N];
char str[10];

void change(int k,int l,int r,int v1,int v2,int pos)
{
	if (l==r)
	{
		sum[k][1]+=(ll)v1;
		add[k]+=v2;
		return;
	}
	int mid=l+r>>1;
	if (pos<=mid) change(k<<1,l,mid,v1,v2,pos);
	else change(k<<1|1,mid+1,r,v1,v2,pos);
	
	add[k]=add[k<<1]+add[k<<1|1];
	for (register int i=0; i<=4; ++i)
	{
		int now=((i-add[k<<1])%5+5)%5;
		sum[k][i]=sum[k<<1][i]+sum[k<<1|1][now];
	}
}

int main(){
	scanf("%d",&n);
	for (register int i=1; i<=n; ++i)
	{
		scanf("%s",str+1);
		if (str[1]=='a')
		{
			scanf("%d",&num[i].x); num[i].f=1;
			b[++tot]=num[i].x;
		}
		else if (str[1]=='d')
		{
			scanf("%d",&num[i].x); num[i].f=-1;
		}
		else
		{
			num[i].f=0;
		}
	}
	sort(b+1,b+tot+1);
	tot=unique(b+1,b+tot+1)-b-1;
	for (register int i=1; i<=n; ++i)
	{
		if (!num[i].f) printf("%lld\n",sum[1][3]);
		else
		{
			int v1=num[i].x*num[i].f;
			int v2=num[i].f;
			int pos=lower_bound(b+1,b+tot+1,num[i].x)-b;
			change(1,1,n,v1,v2,pos);
		}
	}
return 0;
}
发布了64 篇原创文章 · 获赞 29 · 访问量 666

猜你喜欢

转载自blog.csdn.net/Dove_xyh/article/details/104057273
今日推荐