bzoj4742 NOI2017整数

Posted nicodafagood

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj4742 NOI2017整数相关的知识,希望对你有一定的参考价值。

不会考试题来补一手题解

题目背景

在人类智慧的山巅,有着一台字长为10485761048576 位(此数字与解题无关)的超级计算机,著名理论计算机科

学家P博士正用它进行各种研究。不幸的是,这天台风切断了电力系统,超级计算机

无法工作,而 P 博士明天就要交实验结果了,只好求助于学过OI的你. . . . . .

题目描述

P 博士将他的计算任务抽象为对一个整数的操作。

具体来说,有一个整数xx ,一开始为00 。

接下来有nn 个操作,每个操作都是以下两种类型中的一种:

  • 1 a b:将xx 加上整数a\cdot 2^ba?2b ,其中aa 为一个整数,bb 为一个非负整数

  • 2 k :询问xx 在用二进制表示时,位权为2^k2k 的位的值(即这一位上的11 代表 2^k2k )

保证在任何时候,x\geqslant 0x?0 。

输入输出格式

输入格式:

 

输入的第一行包含四个正整数n,t_1,t_2,t_3n,t1?,t2?,t3? ,nn 的含义见题目描述,t_1t1? ,t_2t2? ,t_3t3? 的具体含义见子任务。

接下来nn 行,每行给出一个操作,具体格式和含义见题目描述。

同一行输入的相邻两个元素之间,用恰好一个空格隔开。

 

输出格式:

 

对于每个询问操作,输出一行,表示该询问的答案(00 或11 )。对于加法操作,没有任何输出。

说明

n<=1e6  

a<=1e9

b<= 30*n

 

考虑一般做法

建一颗大小为30*n的线段树

将a的每一位拆开,然后在线段树里找下一个1或0

复杂度O(30n*log30n)

看起来很慢

其实常数够优秀的话是可以A的

对于非wys选手来说

其实不需要一位一位的计算

一个叶子节点记录30位

将a拆成2个数

因为进位退位最多为1

在线段树上面找出一个点以后最长连续一段的0或者(1<<30)-1

然后就做完了

复杂度O(nlogn)

代码不长,挺好写的,就300行左右,也就是一个上午的时间就可以对着大数据调出来啦

技术分享图片
//%std
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
#define lovelive long long
#define lc son[x][0]
#define rc son[x][1]
#define lowbit(x) (x&(-x))
#define pt vc
const int N=1e6+100; 
void read(int &x)
{
  int p=1;
  x=0;
  char c=getchar();
  while(c<0||c>9)
  {
    if(c==-)
      p=-1;
    c=getchar();
  }
  while(c>=0&&c<=9)
  {
      x=x*10+c-48;
      c=getchar();
  }
  x*=p;
}
struct tree{
  int l,r;
  bool sum1,sum0;
  int lazy,key;
}t[N*4];

void buildtree(int i,int l,int r)
{
  t[i].l=l;
  t[i].r=r;
  t[i].lazy=-1;
  t[i].sum0=1;
  if(l==r)
    return;
  int mid=(l+r)>>1;
  buildtree(i<<1,l,mid);
  buildtree(i<<1|1,mid+1,r);
}
void pushdown(int i)
{
  if(t[i].lazy==-1)
    return;
  t[i<<1].sum0=t[i<<1|1].sum0=t[i].lazy^1;
  t[i<<1].sum1=t[i<<1|1].sum1=t[i].lazy;
  t[i<<1].lazy=t[i<<1|1].lazy=t[i].lazy;
  if(t[i<<1].l==t[i<<1].r)
    t[i<<1].key=t[i].lazy? (1<<30)-1:0;
  if(t[i<<1|1].l==t[i<<1|1].r)
    t[i<<1|1].key=t[i].lazy? (1<<30)-1:0;
  t[i].lazy=-1;
}

void change(int i,int pos,int x)
{
  if(t[i].l>pos||t[i].r<pos)
    return;
  if(t[i].l==t[i].r)
  {
      t[i].sum0=t[i].sum1=0;
      t[i].key=x;
      if(x==((1<<30)-1))
        t[i].sum1=1;
      else
        if(!x)
          t[i].sum0=1;
      return;
  }
  pushdown(i);
  change(i<<1,pos,x);
  change(i<<1|1,pos,x);
  t[i].sum1=t[i<<1].sum1&t[i<<1|1].sum1;
  t[i].sum0=t[i<<1].sum0&t[i<<1|1].sum0;
}
void change_(int i,int l,int r,int x)
{
  if(l>t[i].r||r<t[i].l)
    return;
  if(l<=t[i].l&&t[i].r<=r)
  {
    t[i].sum1=x;
    t[i].sum0=x^1;
    t[i].lazy=x;
    if(t[i].l==t[i].r)
      t[i].key=x? (1<<30)-1:0;
    return;
  }
  pushdown(i);
  change_(i<<1,l,r,x);
  change_(i<<1|1,l,r,x);
  t[i].sum1=t[i<<1].sum1&t[i<<1|1].sum1;
  t[i].sum0=t[i<<1].sum0&t[i<<1|1].sum0;
}

int find_key(int pos)
{
  int i=1;
  while(1)
  {
    if(t[i].l==t[i].r)
      return t[i].key;
    pushdown(i);
    if(pos<=t[i<<1].r)
      i=i<<1;
    else
      i=i<<1|1;
  }
 } 
int find_num1(int pos)
{
  int i=1,r=1,pre;
  while(t[i].l!=t[i].r)
  {
      pushdown(i);
    if(pos<=t[i<<1].r)
      i=i<<1;
    else
      i=i<<1|1;
  }
  if(!t[i].sum1)
    return 0;
  pre=i;
  i>>=1;
  while(i!=1)
  {
    if(!(pre&1))
    {
      if(!t[pre^1].sum1)
        break;
      else
          r+=t[pre^1].r-t[pre^1].l+1;
    }
    pre=i;
    i>>=1;
  }
  if(pre==(i<<1|1))
    return r;
  i=i<<1|1;
  while(1)
  {
    if(t[i].l==t[i].r)
    {
      r+=t[i].sum1;
      break;
    }
    pushdown(i);
      if(t[i<<1].sum1)
        r+=t[i<<1].r-t[i<<1].l+1,i=i<<1|1;
      else
        i=i<<1;
  }
  return r;
}
int find_num0(int pos)
{
  int i=1,r=1,pre;
  while(t[i].l!=t[i].r)
  {
      pushdown(i); 
    if(pos<=t[i<<1].r)
      i=i<<1;
    else
      i=i<<1|1;
  }
  if(!t[i].sum0)
    return 0;
  pre=i;
  i>>=1;
  while(i!=1)
  {
    if(!(pre&1))
    {
      if(!t[pre^1].sum0)
        break;
      else
          r+=t[pre^1].r-t[pre^1].l+1;
    }
    pre=i;
    i>>=1;
  }
  if(pre==(i<<1|1))
    return r;
  i=i<<1|1;
  while(1)
  {
    if(t[i].l==t[i].r)
    {
      r+=t[i].sum0;
      break;
    }
    pushdown(i);
      if(t[i<<1].sum0)
        r+=t[i<<1].r-t[i<<1].l+1,i=i<<1|1;
      else
        i=i<<1;
  }
  return r;
}
void add(int pos,int a)
{
  int tmp=find_key(pos);
  if(tmp+a<(1<<30))
    change(1,pos,tmp+a);
  else
  {
    change(1,pos,tmp+a-(1<<30));
    tmp=find_num1(pos+1);
    if(tmp)
      change_(1,pos+1,pos+tmp,0);
    change(1,pos+tmp+1,find_key(pos+tmp+1)+1);
  }
}
void del(int pos,int a)
{
  int tmp=find_key(pos);
  if(tmp>=a)
    change(1,pos,tmp-a);
  else
  {
    change(1,pos,tmp-a+(1<<30));
    tmp=find_num0(pos+1);
    if(tmp)
      change_(1,pos+1,pos+tmp,1);
    change(1,pos+tmp+1,find_key(pos+tmp+1)-1);
  }
}
int main()
{
  int n,fk,opt,a,b,x,y,tmp,a1,a2;
//  freopen("test.in","r",stdin);
//  freopen("test.out","w",stdout);
  read(n);read(fk);read(fk);read(fk);
  buildtree(1,1,n+1);
  for(int i=1;i<=n;i++)
  {
    if(i==4501)
      i--,i++;
      read(opt);
      if(opt&1)
      {
      read(a);read(b);
      x=b/30+1;
      y=b%30;
      if(a<0)
      {
          a=-a; 
        a1=(a<<y)&((1<<30)-1);
        a2=a>>(30-y);
        del(x,a1);
        if(a2)
          del(x+1,a2);
      }
      else
      {
          a1=(a<<y)&((1<<30)-1);
        a2=a>>(30-y);
        add(x,a1);
        if(a2)
          add(x+1,a2);
      }
    }
    else
    {
      read(a);
      x=a/30+1;
      y=a%30;
      tmp=find_key(x);
      cout<<(tmp>>y&1)<<"\n";
    }
  }
  return 0;
}
/*
10000 1 1 1
1 1 30000
1 1 100
1 -1 1000
1 1 10000
2 10000

10000 1 1 1
1 1 30000
1 -1 1000
1 1 10000
2 10000
*/
View Code

 

以上是关于bzoj4742 NOI2017整数的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ4942 & UOJ314:[NOI2017]整数——题解

[BZOJ4942][NOI2017]整数(线段树+压位)

BZOJ 4742: [Usaco2016 Dec]Team Building

BZOJ1509[NOI2003]逃学的小孩 直径

bzoj 1493: [NOI2007]项链工厂(线段树)

BZOJ4945[Noi2017]游戏 2-SAT