2018.1.4

今天学了树状数组,顺便切了几道很简单的题。

引子:lowbit(i)表示一个二进制数最低位的1代表的数。
求lowbit:a&~a (a得有符号)
树状数组d[i]中存的就是原数组下标前面lowbit(i)个数字的和。

然后省略一大堆blabla的关于树状数组的东西。

1:树状数组模板

输入一个数列A1,A2….An(1<=N<=100000),在数列上进行M(1<=M<=100000)次操作,操作有以下两种:
(1) 格式为C I X,其中C为字符“C”,I和X(1<=I<=N,|X|<=10000)都是整数,表示把把a[I]改为X
(2) 格式为Q L R,其中Q为字符“Q”,L和R表示询问区间为[ L ,R] (1<=L<=R<=N),表示询问A[L]+…+A[R]的值。
裸的树状数组。

#include<bits/stdc++.h>
using namespace std;
int n;
int a[101000],tr[101000];
int lowbit(int x) {return x&-x;} 
int read() 
{ 
    bool flag=true; 
    int num=0;char c=getchar(); 
    for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=false; 
    for(;c>='0'&&c<='9';c=getchar()) 
    num=(num<<3)+(num<<1)+c-48; 
    if(flag) return num; 
     else return -num; 
} 
void change(int x,int num)
{
    while(x<=n)
     {
      tr[x]+=num;
      x+=lowbit(x);
     }
    return;
}
int find(int x)
{
    long long sum=0;
    while(x>0)
     {
      sum+=tr[x];
      x-=lowbit(x);
     }
    return sum;
}
void work()
{
    n=read();
    for(int i=1;i<=n;++i) a[i]=read(),change(i,a[i]);
    int m=read();
    for(int i=1;i<=m;++i)
     {
       char c;
       for(c=getchar();c<'A'||c>'Z';c=getchar());
       if(c=='C')
        {
         int aa=read(),bb=read();
         change(aa,bb-a[aa]);a[aa]=bb;
        }
       else 
        {
         int a=read(),b=read();
         int ans1=find(a-1);
         int ans2=find(b); 
         printf("%d\n",ans2-ans1);
        } 
     } 
    return;
}
int main()
{
    work();
    return 0;
}

2、星星
题目描述
天文学家经常要检查星星的地图,每个星星用平面上的一个点来表示,每个星星都有坐标。我们定义一个星星的“级别”为给定的星星中不高于它并且不在它右边的星星的数目。天文学家想知道每个星星的“级别”。

                        5
                      *
                    4
                  *
                1       2   3
              *       *   *

例如上图,5号星的“级别”是3(1,2,4这三个星星),2号星和4号星的“级别”为1。

给你一个地图,你的任务是算出每个星星的“级别”。

输入格式
输入的第一行是星星的数目N(1<=N<=60000),接下来的N行描述星星的坐标(每一行是用一个空格隔开的两个整数X,Y,0<=X,Y<=32000)。

星星的位置互不相同。星星的描述按照Y值递增的顺序列出,Y值相同的星星按照X值递增的顺序列出。

输出格式
输出包含N行,一行一个数。第i行是第i个星星的“级别”

input

5
1 1
5 1
7 1
3 3
5 5
output

0
1
2
1
3
数据规模与约定
时间限制:1s
空间限制:256MB

这道题我们得想得到如何用线段数组来做。也就是我们要想到线段数组的下表是横坐标或者纵坐标,而不是编号。

这个输入的限制给了你很大的启发。
只要按输入顺序扫描每个点,这样可以保证已经插入树状数组的点都在当前点正左侧或下侧。 我们只需寻找有多少点位于当前点左侧,处理完当前点后,将其按x坐标插入树状数组,即让d[x]加1。

这道题还有加强版,如果坐标的范围再增大的话需要用离散化。

#include<bits/stdc++.h>
using namespace std;
int n,tr[33000];
struct star 
{
   int x,y; 
}po[61000];
int lowbit(int x) {return x&-x;} 
int read() 
{ 
    bool flag=true; 
    int num=0;char c=getchar(); 
    for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=false; 
    for(;c>='0'&&c<='9';c=getchar()) 
    num=(num<<3)+(num<<1)+c-48; 
    if(flag) return num; 
     else return -num; 
} 
void change(int x,int num)
{
    while(x<=32002)
     {
      tr[x]+=num;
      x+=lowbit(x);
     }
    return;
}
int find(int x)
{
    long long sum=0;
    while(x>0)
     {
      sum+=tr[x];
      x-=lowbit(x);
     }
    return sum;
}
void work()
{
    n=read();
    int rest=0,tot=0;
    for(int i=1;i<=n;++i)
     {
      po[i].x=read()+1;po[i].y=read()+1;
      change(po[i].x,1);
      printf("%d\n",find(po[i].x)-1);
     }
    return;
}
int main()
{
    work();
    return 0;
}

3、区间修改单点查询
题目描述
已知一个数列,你需要进行下面两种操作:

1.将某区间每一个数数加上x

2.求出某一个数的值

输入格式
第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。

第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

接下来M行每行包含2或4个整数,表示一个操作,具体如下:

操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k;

操作2: 格式:2 x 含义:输出第x个数的值。

输出格式
输出包含若干行整数,即为所有操作2的结果。

样例数据
input

5 5
1 5 4 2 3
1 2 4 2
2 3
1 1 5 -1
1 3 5 7
2 4
output

6
10
数据规模与约定
时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=8,M<=10

对于70%的数据:N<=10000,M<=10000

对于100%的数据:N<=500000,M<=500000

这道题我们要想到查分。查分数组的前i项累加和就是第i个数的值。那么也是很裸的线段数组了

#include<bits/stdc++.h>
using namespace std;
int n,m,tr[501000],a[501000];
int lowbit(int x) {return x&-x;} 
int read() 
{ 
    bool flag=true; 
    int num=0;char c=getchar(); 
    for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=false; 
    for(;c>='0'&&c<='9';c=getchar()) 
    num=(num<<3)+(num<<1)+c-48; 
    if(flag) return num; 
     else return -num; 
} 
void change(int x,int num)
{
    while(x<=n)
     {
      tr[x]+=num;
      x+=lowbit(x);
     }
    return;
}
int find(int x)
{
    long long sum=0;
    while(x>0)
     {
      sum+=tr[x];
      x-=lowbit(x);
     }
    return sum;
}
void work()
{
    n=read();m=read();
    int aa,bb;
    aa=read();bb=read();a[1]=aa;a[2]=bb-aa; 
    change(1,a[1]);change(2,a[2]);
    for(int i=3;i<=n;++i)
     {
       aa=bb;bb=read();
       a[i]=bb-aa;
       change(i,a[i]);
     }
    for(int i=1;i<=m;++i)
     {
      int check=read();
      if(check==1)
       {
        int xx=read(),yy=read()+1,zz=read();
        change(xx,zz);
        change(yy,-zz);
       }
      else
       {
        int xx=read();
        printf("%d\n",find(xx));
       }
     }
    return;
}
int main()
{
    work();
    return 0;
}

4、打鼹鼠
题目描述
SuperBrother在机房里闲着没事干(再对比一下他的NOIP,真是讽刺啊……),于是便无聊地开始玩“打鼹鼠”……

在这个“打鼹鼠”的游戏中,鼹鼠会不时地从洞中钻出来,不过不会从洞口钻进去(鼹鼠真胆大……)。洞口都在一个大小为n(n<=1024)的正方形中。这个正方形在一个平面直角坐标系中,左下角为(0,0),右上角为(n-1,n-1)。洞口所在的位置都是整点,就是横纵坐标都为整数的点。而SuperBrother也不时地会想知道某一个范围的鼹鼠总数。这就是你的任务。

输入格式
每个输入文件有多行。

第一行,一个数n,表示鼹鼠的范围。

以后每一行开头都有一个数m,表示不同的操作:

m=1,那么后面跟着3个数x,y,k(0<=x,y

#include<bits/stdc++.h>
using namespace std;
int n,m,tr[1030][1030];
int lowbit(int x) {return x&-x;} 
int read() 
{ 
    bool flag=true; 
    int num=0;char c=getchar(); 
    for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=false; 
    for(;c>='0'&&c<='9';c=getchar()) 
    num=(num<<3)+(num<<1)+c-48; 
    if(flag) return num; 
     else return -num; 
} 
void change(int x,int y,int num)
{
    int t;
    while(x<=n)
     { 
      t=y;
      while(t<=n)
       {
        tr[x][t]+=num;
        t+=lowbit(t);
       }
      x+=lowbit(x);
     }
    return;
}
long long find(int x,int y)
{
    long long sum=0;
    int t;
    while(x>0)
     {
      t=y;
      while(t>0)
       {
        sum+=tr[x][t];
        t-=lowbit(t);
       }
      x-=lowbit(x);
     }
    return sum;
}
void work()
{
    n=read();
    int check=read();
    while(check!=3)
     {
      if(check==1)
       {
        int xx=read()+1,yy=read()+1,zz=read();
        change(xx,yy,zz);
       }    
      else
       {
        int x1=read()+1,y1=read()+1,x2=read()+1,y2=read()+1;
        printf("%d\n",find(x2,y2)-find(x1-1,y2)-find(x2,y1-1)+find(x1-1,y1-1));
       }
      check=read();
     }
}
int main()
{
    work();
    return 0;
}

5、zjoi2003密码机
题目描述
一台密码机按照以下的方式产生密码:首先往机器中输入一系列数,然后取出其中一部分数,将它们异或以后得到一个新数作为密码。现在请你模拟这样一台密码机的运行情况,用户通过输入控制命令来产生密码。 密码机中存放了一个数列,初始时为空。密码机的控制命令共有3种:

ADD 把加入到数列的最后。

REMOVE 在数列中找出第一个等于的数,把它从数列中删除。

XOR BETWEEN AND 对于数列中所有大于等于并且小于等于的数依次进行异或,输出最后结果作为密码。如

果只有一个数满足条件,输出这个数。如果没有任何数满足条件,输出0。

你可以假设用户不会REMOVE一个不存在于数列中的数,并且所有输入的数都不超过20000。

输入格式
输入文件password.in包括了一系列的控制命令。每个控制命令占据单独一行。输入文件中没有多余的空行。文件不超过60000行。

输出格式
对于每个XOR命令,依次在password.out中输出一行包括你的密码机所产生的密码。输出文件中不应该包含任何的多余字符。

input

ADD 5
ADD 6
XOR BETWEEN 1 AND 10
REMOVE 5
XOR BETWEEN 6 AND 8
output

3
6
数据规模与约定
1s

时间限制:1s 空间限制:256MB

include<bits/stdc++.h>
using namespace std;
int n,m,tr[23000];
int a[23000];
int lowbit(int x) {return x&-x;} 
int read() 
{ 
    bool flag=true; 
    int num=0;char c=getchar(); 
    for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=false; 
    for(;c>='0'&&c<='9';c=getchar()) 
    num=(num<<3)+(num<<1)+c-48; 
    if(flag) return num; 
     else return -num; 
} 
void change(int x,int num)
{
    while(x<=20000)
     {
      tr[x]^=num;
      x+=lowbit(x);
     }
    return;
}
int find(int x)
{
    long long sum=0;
    while(x>0)
     {
      sum^=tr[x];
      x-=lowbit(x);
     }
    return sum;
}
void work()
{
    char c;
    while(scanf("%c",&c)==1)
     {
      if(c=='A')
       {
        int x=read();
        a[x]++;
        change(x,x);
       }
      if(c=='X')
       {
        int x=read(),y=read();
        if(x>y) {printf("0\n");continue;}
        printf("%d\n",find(x-1)^find(y));
       }
      if(c=='R')
       {
        int x=read();
        if(a[x]>0) change(x,x),a[x]--;
       }
     }
    return;
}
int main()
{
    work();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/a1035719430/article/details/78975401