hdu4288(离线+离散化+线段树)

题目

维护一个从小到大的数列,可以添加和删除数据,询问每个数列下标%5=3的数据的值的和

题解

一开始,一直以为是没排序的,想了好久才发现是排序好的,稍微简单了点
离散化的操作比较简单,用STL中的lower_bound和unique两个函数可以轻松完成
离线的原因是因为线段树的局限性,无法完成删除或者增加节点的操作,因为线段树的大小从一开始必须就是固定的,所以用离线的方式,我们就能够知道数据的数量,按照所有数据的总数建立线段树,这就完成了增加线段树的“增加”操作的变形,删除操作我们可以用一个cnt来记录节点的子节点数,删除操作就是将底层的叶子节点减为0,然后将cnt-1,这样这个节点实际上还是存在的,但因为数值为0,不影响到我们的后续总和计算,用cnt这个变量来消除他对数列下标的影响
线段树的话,没啥难度,就是一个单点更新的线段树,要注意的是左子树加右子树时,需要对右子树的下标进行还原,因为例如右子树的第一个在总数列中的位置就应该是(1+sizeof(左子树)),这个时候cnt变量就起到至关重要的作用了

代码

#include
#include
#include
#include
#define maxn 100010
#define lson rt<<1
#define rson rt<<1|1
#define ll long long int
using namespace std;
int a[maxn],b[maxn],T;
char p[maxn],c[5];
struct Tre
{
	int l,r,cnt;
	ll sum[5];
}tre[maxn<<2];
void pushup(int rt)
{
	for(int i=0;i<5;i++)
	tre[rt].sum[i]=tre[lson].sum[i]+tre[rson].sum[((i-tre[lson].cnt)%5+5)%5];
}
void build(int rt,int l,int r)
{
	tre[rt].l=l;
	tre[rt].r=r;
	tre[rt].cnt=0;
	for(int i=0;i<5;i++) tre[rt].sum[i]=0;
	if(l==r) return;
	int mid=(l+r)>>1;
	build(lson,l,mid);
	build(rson,mid+1,r);
}
void update(int rt,int pos,int add)
{
	tre[rt].cnt+=add;
	if(tre[rt].l==tre[rt].r){
		tre[rt].sum[1]+=add*b[pos-1];
		return ;
	}
	int mid=(tre[rt].l+tre[rt].r)>>1;
	if(pos<=mid) update(lson,pos,add);
	else update(rson,pos,add);
	pushup(rt);
}
int main()
{
	//freopen("1.txt","r",stdin);
	int k,j;
	while(scanf("%d",&T)==1){
		k=0;j=0;
		int f;
		for(int i=1;i<=T;i++){
			scanf("%s",c);
			p[i]=c[0];
			if(p[i]!='s'){
				scanf("%d",&a[k++]);
				b[k-1]=a[k-1];
			}
		}
		sort(b,b+k);
		int n=unique(b,b+k)-b;
		build(1,1,n);
		for(int i=1;i<=T;i++){
			if(p[i]=='s'){
				printf("%I64d\n",tre[1].sum[3]);
				continue;
			}
			if(p[i]=='a') f=1;
			else f=-1;
			int pos=lower_bound(b,b+n,a[j++])-b+1;
			update(1,pos,f);
		}
	}
	//fclose(stdin);
}

你可能感兴趣的:(#,ACM——线段树)