NOI 2017 整数

好玄学的线段树啊...

调了半天的题,最后发现是传参的数据类型传错了(long long 传成了int),结果RE3小时...

说下思路吧...

其实主题思想很简单,就是把一个二进制数作为一个序列建立一棵线段树,然后各种维护即可

当然这样会TLE或MLE之类的

所以我们采用其他的策略:压位!!!

这里我选择压60位。

然后我们考虑:对一次修改操作,我们如何处理?

首先,我们看一下这个操作会被分在哪一组里。

很显然,如果这一次操作为a·2^b,那么一定会被分在第b/60组里!(或者至少有一部分在第b/60组里)!

那么就好办了,我们把会被分在这一组里的部分加到这一组里即可

那么谁是会被分到这一组里的部分呢?

自然是(a·2^(b mod 60))mod (2^60-1)这些了!

等等,那剩下的部分怎么办?

换句话说,我们只是把可能在这部分里的值加了进去,那万一a特别大,一部分值超越了分到的部分呢?

对于这一部分,根据数据范围,我们可以确定的是,我们压60位以后,一个幂次已经被取模后的修改不会跨越两个相邻块!

那我们就把剩余部分放到下一块就好了

减法也就是同理啦

所以我们的操作长这样:

ll typ=read();
        if(typ==1)
        {
            ll a=read(),b=read();
            int p1=b/seed,p2=b%seed;
            if(a>0)
            {
                ll x=(a<>=(seed-p2);
                if(b)
                {
                    add(p1,a);
                }
            }else
            {
                a=-a;
                ll x=(a<>=(seed-p2);
                if(b)
                {
                    sub(p1,a);
                }
            }
        }

接下来就是最复杂的部分了:如何进行修改?

这就涉及到线段树的维护了。

对于每个节点,我们存储以下几个信息:

第一:这个节点是否是全1

第二:这个节点是否是全0

第三:表示这个节点性质的lazy标签

然后我们进行维护:

首先我们假设进行的是加法,那么我们首先要在这个块里进行修改,修改很简单,就是对这个块加上你扔进来的值即可。

可是,这样会产生几个问题:

第一:如果加爆了怎么办?

显然涉及到进位问题啊。

这个问题我们稍后再解决。

第二:怎么进行单点修改?

把单点修改转成区间修改,然后修改一条树链即可。

第三:怎么知道你要修改的点的值?

我们维护一个data数组存储,查询时在线段树上查询即可

单点修改:

void change(int rt,ll val)
{
    if(posi[rt]!=-1)
    {
        data[posi[rt]]=val;
    }
    if(val==0)
    {
        tree[rt].tag[0]=1;
        tree[rt].tag[1]=0;
        tree[rt].lazy=0;
    }else if(val==INF)
    {
        tree[rt].tag[0]=0;
        tree[rt].tag[1]=1;
        tree[rt].lazy=INF;
    }else
    {
        tree[rt].tag[0]=tree[rt].tag[1]=0;
        tree[rt].lazy=-1;
    }
}

区间操作:

void ins_range(int rt,int l,int r,ll val)
{
    if(ls>=l&&rs<=r)
    {
        change(rt,val);
        return;
    }
    pushdown(rt);
    int mid=(ls+rs)>>1;
    if(mid>=l)
    {
        ins_range(rt1,l,r,val);
    }
    if(mid

上传和下传:

void pushdown(int rt)
{
    if(tree[rt].lazy!=-1)
    {
        change(rt1,tree[rt].lazy);
        change(rt2,tree[rt].lazy);
        tree[rt].lazy=-1;
    }
}
void pushup(int rt)
{
    tree[rt].tag[0]=(tree[rt1].tag[0]&tree[rt2].tag[0]);
    tree[rt].tag[1]=(tree[rt1].tag[1]&tree[rt2].tag[1]);
}

查询某一点:

ll query(int rt,int posii)
{
    if(ls==rs)
    {
        return data[ls];
    }
    pushdown(rt);
    int mid=(ls+rs)>>1;
    if(posii<=mid)
    {
        return query(rt1,posii);
    }else
    {
        return query(rt2,posii);
    }
}

接下来我们讨论加爆了的情况

如果加爆了,向前进位也只会进1位,那我们再对前面第一个不全为1的块去+1即可

等等,怎么找到这种块?

利用上面的标记,进行查找即可。

查找操作:

int findf(int rt,int posii,int typ)
{
    if(typ==1&&tree[rt].tag[0])
    {
        return -1;
    }else if(typ==0&&tree[rt].tag[1])
    {
        return -1;
    }
    if(ls==rs)
    {
        return ls;
    }
    pushdown(rt);
    int mid=(ls+rs)>>1;
    int t;
    if(posii<=mid)
    {
        t=findf(rt1,posii,typ);
        if(t!=-1)
        {
            return t; 
        }
    }
    return findf(rt2,posii,typ);
}

最后加减法就是聊尽人事了:

void add(int posii,ll val)
{
    ll temp=query(1,posii);
    ins_range(1,posii,posii,(temp+val)&INF);
    if(temp+val>INF)
    {
        int pos=findf(1,posii+1,0);
        ins_range(1,pos,pos,data[pos]+1);
        if(pos-1>=posii+1)
        {
            ins_range(1,posii+1,pos-1,0);
        }
    }
}
void sub(int posii,ll val)
{
    ll temp=query(1,posii);
    ins_range(1,posii,posii,(temp-val)&INF);
    if(temp-val<0)
    {
        int pos=findf(1,posii+1,1);
        ins_range(1,pos,pos,data[pos]-1);
        if(pos-1>=posii+1)
        {
            ins_range(1,posii+1,pos-1,INF);
        }
    }
}

最后,全代码:


#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define ls tree[rt].lson
#define rs tree[rt].rson
#define rt1 rt<<1
#define rt2 (rt<<1)|1
#define ll long long
using namespace std;
const ll seed=60;
const ll INF=(1ll<<60)-1;
struct Tree
{
    int lson;
    int rson;
    int tag[2];
    ll lazy;
}tree[2000005];
int posi[2000005];
ll data[2000005];
void buildtree(int rt,int l,int r)
{
    tree[rt].lson=l;
    tree[rt].rson=r;
    tree[rt].tag[0]=1;
    tree[rt].lazy=-1;
    posi[rt]=-1;
    if(l==r)
    {
        posi[rt]=l;
        return;
    }
    int mid=(l+r)>>1;
    buildtree(rt1,l,mid);
    buildtree(rt2,mid+1,r);
}
void change(int rt,ll val)
{
    if(posi[rt]!=-1)
    {
        data[posi[rt]]=val;
    }
    if(val==0)
    {
        tree[rt].tag[0]=1;
        tree[rt].tag[1]=0;
        tree[rt].lazy=0;
    }else if(val==INF)
    {
        tree[rt].tag[0]=0;
        tree[rt].tag[1]=1;
        tree[rt].lazy=INF;
    }else
    {
        tree[rt].tag[0]=tree[rt].tag[1]=0;
        tree[rt].lazy=-1;
    }
}
void pushdown(int rt)
{
    if(tree[rt].lazy!=-1)
    {
        change(rt1,tree[rt].lazy);
        change(rt2,tree[rt].lazy);
        tree[rt].lazy=-1;
    }
}
void pushup(int rt)
{
    tree[rt].tag[0]=(tree[rt1].tag[0]&tree[rt2].tag[0]);
    tree[rt].tag[1]=(tree[rt1].tag[1]&tree[rt2].tag[1]);
}
void ins_range(int rt,int l,int r,ll val)
{
    if(ls>=l&&rs<=r)
    {
        change(rt,val);
        return;
    }
    pushdown(rt);
    int mid=(ls+rs)>>1;
    if(mid>=l)
    {
        ins_range(rt1,l,r,val);
    }
    if(mid>1;
    if(posii<=mid)
    {
        return query(rt1,posii);
    }else
    {
        return query(rt2,posii);
    }
}
int findf(int rt,int posii,int typ)
{
    if(typ==1&&tree[rt].tag[0])
    {
        return -1;
    }else if(typ==0&&tree[rt].tag[1])
    {
        return -1;
    }
    if(ls==rs)
    {
        return ls;
    }
    pushdown(rt);
    int mid=(ls+rs)>>1;
    int t;
    if(posii<=mid)
    {
        t=findf(rt1,posii,typ);
        if(t!=-1)
        {
            return t; 
        }
    }
    return findf(rt2,posii,typ);
}
void add(int posii,ll val)
{
    ll temp=query(1,posii);
    ins_range(1,posii,posii,(temp+val)&INF);
    if(temp+val>INF)
    {
        int pos=findf(1,posii+1,0);
        ins_range(1,pos,pos,data[pos]+1);
        if(pos-1>=posii+1)
        {
            ins_range(1,posii+1,pos-1,0);
        }
    }
}
void sub(int posii,ll val)
{
    ll temp=query(1,posii);
    ins_range(1,posii,posii,(temp-val)&INF);
    if(temp-val<0)
    {
        int pos=findf(1,posii+1,1);
        ins_range(1,pos,pos,data[pos]-1);
        if(pos-1>=posii+1)
        {
            ins_range(1,posii+1,pos-1,INF);
        }
    }
}
inline ll read()
{
    ll f=1,x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,whatever1,whatever2,whatever3;
int main()
{
    n=read(),whatever3=read(),whatever2=read(),whatever1=read();
    buildtree(1,0,n/2+2);
    while(n--)
    {
        ll typ=read();
        if(typ==1)
        {
            ll a=read(),b=read();
            int p1=b/seed,p2=b%seed;
            if(a>0)
            {
                ll x=(a<>=(seed-p2);
                if(b)
                {
                    add(p1,a);
                }
            }else
            {
                a=-a;
                ll x=(a<>=(seed-p2);
                if(b)
                {
                    sub(p1,a);
                }
            }
        }else
        {
            ll q=read();
            printf("%lld\n",(query(1,q/seed)>>(q%seed))&1);
        }
    }
}

 

你可能感兴趣的:(线段树,数据结构,高精度,压位)