您的位置:首页 > 其它

HDU-4288:Coder(线段树+离线操作)

2017-08-09 21:07 239 查看
题目链接:点击打开链接

题目大意:

给你一些数让你依次添加到一个集合中,集合中的数是递增的。并且中间会求集合中下标mod5=3的数的和

解题思路:

自己完全不会做,表示线段树怎么能用来写这种东西。

后来还是给大神跪了。据说可以暴力压时间水过,不过也没尝试。

具体的思路就是先将所有数离线保存起来,然后依次再加入线段树中,具体操作也说不太清楚。

中间有一步左加右减的操作。很神奇。先将代码搬过来 t[rt].sum[i]=t[lson].sum[i]+t[rson].sum[(i-t[lson].cnt%5+5)%5];  cnt表示当前节点有效数字

可以看到当前节点 mod5=i 的值是由左节点 mod5=i 的值+右节点 mod5=x(暂且用x表示)。x=(i-t[lson].cnt%5+5)%5

这部分可能有些难以理解,举个例子说明吧   假设现在左节点有4个有效数字 ,右节点有4个有效数字。  那么他们在集合中的下标理论上是 1到8.

假设现在求 mod5=3 的值。那么左儿子就要取 下标mod5=3 的那些数,左儿子下标是正常从1开始的。但是右儿子的有效数字虽然集合中的下标是从5到8,但是在它的线段树中实际上还是1到4,那么我们当前节点mod5=3的数应该有两个 下标为3和下标为8。但是右儿子现在下标不是从5开始,所以就需要转化一下即  x=(i-t[lson].cnt%5+5)%5

下面贴代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
#include <algorithm>
#include <set>
#include <functional>
#define rank ra
#define lson rt<<1
#define rson rt<<1|1
#define pb push_back
using namespace std;
typedef long long ll;
int n,m,ans;
int a[100010],v[100010];
char c[100010][5];
struct node
{
int l,r,mid;
int cnt; //记录有效数字的个数
ll sum[5]; //分别mod5=i的和
}t[500000];
void pushup(int rt)
{
t[rt].cnt=t[lson].cnt+t[rson].cnt; //更新有效数字
for(int i=0;i<5;i++) //更新当前值得和
t[rt].sum[i]=t[lson].sum[i]+t[rson].sum[(i-t[lson].cnt%5+5)%5];
}
void build(int l,int r,int rt) //初始化
{
int mid=(l+r)>>1;
t[rt].l=l;t[rt].r=r;
t[rt].mid=mid;
for(int i=0;i<5;i++)
t[rt].sum[i]=0;
t[rt].cnt=0;
if(l==r)
return ;
build(l,mid,lson);
build(mid+1,r,rson);
}
void update(int idx,int flag,int rt)
{
if(t[rt].l==t[rt].r)
{
if(flag) //更新操作
{
t[rt].sum[1]=v[idx];
t[rt].cnt=1;
}
if(flag<0)
{
t[rt].sum[1]=0;
t[rt].cnt=0;
}
return ;
}
if(idx<=t[rt].mid)
update(idx,flag,lson);
if(idx>t[rt].mid)
update(idx,flag,rson);
pushup(rt);
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
int p=0;
for(int i=1;i<=n;i++)
{
scanf(" %s",c[i]);
if(c[i][0]!='s')
{
scanf("%d",&a[i]);
v[++p]=a[i]; //离线储存
}
}
int k=1;
sort(v+1,v+1+p); //排序去重
for(int i=2;i<=p;i++)
{
if(v[i]!=v[i-1])
v[++k]=v[i];
}
build(1,k,1);
for(int i=1;i<=n;i++)
{
if(c[i][0]=='s')
cout<<t[1].sum[3]<<endl;
int idx=lower_bound(v+1,v+1+k,a[i])-v; //找到大于等于它的最小的数所在位置
if(c[i][0]=='a')
update(idx,1,1);
if(c[i][0]=='d')
update(idx,-1,1);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: