今天学了树状数组,顺便切了几道很简单的题。
引子: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;
}