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

来源:互联网 发布:windows启动盘制作 编辑:程序博客网 时间:2024/05/21 06:00


题目链接:点击打开链接


题目大意:

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