首页 > 其他 > 详细

LOJ 2302 「NOI2017」整数——压位线段树

时间:2019-06-04 12:31:17      阅读:120      评论:0      收藏:0      [点我收藏+]

题目:https://loj.ac/problem/2302

压30位,a最多落在两个位置上,拆成两次操作。

该位置加了 a 之后,如果要进位或者借位,查询一下连续一段 0 / 1 ,修改掉,再在含有 1 / 0 的那个位置上 -1 或者 +1 。

注意是在那个位置上 -1 或者 +1 而不是 -lowbit 或者 +lowbit 。

询问都是 <=30n ,所以只维护 30n 的范围即可。注意线段树压 30 位,开 n 个位置恰好是 0*n ~ 29*n,所以开 n+1 个位置。

线段树只需维护自己区间是全 0 / 1 还是都有。找连续一段 0/1 就在线段树上二分即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ls Ls[cr]
#define rs Rs[cr]
using namespace std;
int rdn()
{
  int ret=0;bool fx=1;char ch=getchar();
  while(ch>9||ch<0){if(ch==-)fx=0;ch=getchar();}
  while(ch>=0&&ch<=9)ret=ret*10+ch-0,ch=getchar();
  return fx?ret:-ret;
}
int Mx(int a,int b){return a>b?a:b;}
int Mn(int a,int b){return a<b?a:b;}
const int N=2e6+5,bs=30;
int n,nr,tot,Ls[N],Rs[N],lx[N],vl[N],tg[N];
int bin[bs+5],U;  bool fx;
void build(int l,int r,int cr)
{
  tg[cr]=-1; lx[cr]=vl[cr]=0;
  if(l==r)return; int mid=l+r>>1;
  ls=++tot; build(l,mid,ls);
  rs=++tot; build(mid+1,r,rs);
}
void pshd(int cr)
{
  if(tg[cr]==-1)return; int k=tg[cr]; tg[cr]=-1;
  tg[ls]=tg[rs]=k;
  if(k==1){ vl[ls]=vl[rs]=U; lx[ls]=lx[rs]=1;}
  else { vl[ls]=vl[rs]=0; lx[ls]=lx[rs]=0;}
}
void pshp(int cr)
{
  if((!lx[ls])&&(!lx[rs]))lx[cr]=0;
  else if(lx[ls]==1&&lx[rs]==1)lx[cr]=1; else lx[cr]=2;
}
void updt(int cr)
{ if(!vl[cr])lx[cr]=0; else if(vl[cr]==U)lx[cr]=1; else lx[cr]=2;}
int fnd(int l,int r,int cr,int L,int R,int k)
{
  if(l>=L&&r<=R)
    {
      if(lx[cr]==(!k))return nr+1; if(l==r)return l;
      int mid=l+r>>1; pshd(cr);
      if(lx[ls]!=(!k))return fnd(l,mid,ls,L,R,k);
      return fnd(mid+1,r,rs,L,R,k);
    }
  int mid=l+r>>1,d=nr+1; pshd(cr);
  if(L<=mid)d=fnd(l,mid,ls,L,R,k);
  if(d==nr+1)d=fnd(mid+1,r,rs,L,R,k);
  return d;
}
void mdfy(int l,int r,int cr,int L,int R,int k)
{
  if(l>=L&&r<=R)
    {
      tg[cr]=k; if(k==1){vl[cr]=U;lx[cr]=1;}
      else {vl[cr]=0;lx[cr]=0;} return;
    }
  int mid=l+r>>1; pshd(cr);
  if(L<=mid)mdfy(l,mid,ls,L,R,k);
  if(mid<R)mdfy(mid+1,r,rs,L,R,k);
  pshp(cr);
}
void mdfy2(int l,int r,int cr,int p,int k)
{
  if(l==r)
    {
      if(k==1)vl[cr]++; else vl[cr]--;/////
      /*if(k==1){ int tmp=(vl[cr]^U); vl[cr]+=(tmp&(-tmp));}
    else vl[cr]-=(vl[cr]&(-vl[cr]));*/
      updt(cr); return;
    }
  int mid=l+r>>1; pshd(cr);
  if(p<=mid)mdfy2(l,mid,ls,p,k);
  else mdfy2(mid+1,r,rs,p,k);
  pshp(cr);
}
void add(int p)
{
  int d=fnd(1,nr,1,p,nr,0);//fnd first 0
  if(p<d)mdfy(1,nr,1,p,d-1,0);
  if(d<=nr)mdfy2(1,nr,1,d,1);//chg 0 to 1
}
void dec(int p)
{
  int d=fnd(1,nr,1,p,nr,1);
  if(p<d)mdfy(1,nr,1,p,d-1,1);
  if(d<=nr)mdfy2(1,nr,1,d,0);
}
void solve(int l,int r,int cr,int p,int k)
{
  if(l==r)
    {
      if(!fx)vl[cr]+=k; else vl[cr]-=k;
      if(vl[cr]>=bin[bs]){ vl[cr]-=bin[bs];add(l+1);}
      if(vl[cr]<0){ vl[cr]+=bin[bs];dec(l+1);}
      updt(cr); return;
    }
  int mid=l+r>>1; pshd(cr);
  if(p<=mid)solve(l,mid,ls,p,k); else solve(mid+1,r,rs,p,k);
  pshp(cr);
}
bool qry(int l,int r,int cr,int p,int p2)
{
  if(l==r)return vl[cr]&bin[p2];
  int mid=l+r>>1; pshd(cr);
  if(p<=mid)return qry(l,mid,ls,p,p2);
  else return qry(mid+1,r,rs,p,p2);
}
int main()
{
  n=rdn(); nr=n+1; int tp=rdn();tp=rdn();tp=rdn();
  bin[0]=1;for(int i=1;i<=bs;i++)bin[i]=bin[i-1]<<1;
  U=bin[bs]-1; tot=1;build(1,nr,1);
  for(int i=1,op,a,b,l,r;i<=n;i++)
    {
      op=rdn();
      if(op==1)
    {
      a=rdn();b=rdn(); l=b; r=b+bs-1;
      l=l/bs+1; r=r/bs+1;
      if(a<0)a=-a,fx=1; else fx=0;
      if(l<r)
        {
          int k=a&(bin[l*bs-b]-1);
          a>>=(l*bs-b); k<<=(b-(l-1)*bs);
          solve(1,nr,1,l,k); solve(1,nr,1,r,a);
        }
      else solve(1,nr,1,l,a);
    }
      else
    {
      a=rdn(); l=a/bs+1; a-=(l-1)*bs;
      printf("%d\n",qry(1,nr,1,l,a));
    }
    }
  return 0;
}

 

LOJ 2302 「NOI2017」整数——压位线段树

原文:https://www.cnblogs.com/Narh/p/10972731.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!