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


题目链接:点击打开链接


题目大意:

给你一些数让你依次添加到一个集合中,集合中的数是递增的。并且中间会求集合中下标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);
        }
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值