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<<p2)&INF;
if(x)
{
add(p1,x);
}
p1++;
a>>=(seed-p2);
if(b)
{
add(p1,a);
}
}else
{
a=-a;
ll x=(a<<p2)&INF;
if(x)
{
sub(p1,x);
}
p1++;
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<r)
{
ins_range(rt2,l,r,val);
}
pushup(rt);
}
上传和下传:
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 <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#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<r)
{
ins_range(rt2,l,r,val);
}
pushup(rt);
}
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);
}
}
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<<p2)&INF;
if(x)
{
add(p1,x);
}
p1++;
a>>=(seed-p2);
if(b)
{
add(p1,a);
}
}else
{
a=-a;
ll x=(a<<p2)&INF;
if(x)
{
sub(p1,x);
}
p1++;
a>>=(seed-p2);
if(b)
{
sub(p1,a);
}
}
}else
{
ll q=read();
printf("%lld\n",(query(1,q/seed)>>(q%seed))&1);
}
}
}