线段树,树状数组(单点操作)

考虑一个无限完整的二叉搜索树(参见下图),节点中的数字是1,2,3,....在根节点为X的子树中,我们可以通过重复获得该子树中的最小数量沿着左边的节点往下走,直到最后一层,我们也可以通过沿着右边的节点找到最大数量。现在给出一些查询,“根节点为X的子树中的最小和最大数目是多少?” 请尝试找到有关查询的答案。 

输入

在输入中,第一行包含一个整数N,表示查询的数量。在接下来的N行中,每行都包含一个代表根号为X(1 <= X <= 2  31  - 1)的子树的数字 

产量

共有N行,其中第i行包含第i个查询的答案。

示例输入

2
8
10

示例输出

1 15
9 11

题意:求以当前为根节点下的最小值节点与最大值节点
思路:乍一看不知道是啥,但是仔细看最小节点最大节点与根节点又有些关系,最小值节点=根节点-x 最大值节点=根节点+x
但是每个对应的x值又不同,说明也与根节点的值有关系
我们就拿几个例子进行比较
我们只需要把用最小值把x求出来,最大值可以把x代进去求出
6 5 化成二进制 110   101
12 9   化成二进制 1100  1001
8 1   化成二进制 1000  0001
我们可以发现每个都是把二进制最低的1去掉再加1,然后我们求二进制最低的1有个特殊的办法 树状数组的lowbit 前不久刚学的23333
所以x=lowbit-1
那么最小值节点=x-lowbit(x)+1 最大值节点=x+lowbit(x)-1
然后这题就简单了
#include<cstdio>
#include<cstring>
using namespace std;
int lowbit(int x)
{
    return x&(-x);
}
int main()
{
    int n,x;
    scanf("%d",&n);
    while(n--)
    {
        scanf("%d",&x);
        printf("%d %d\n",x-lowbit(x)+1,x+lowbit(x)-1);
    }
}
D. Xenia和位操作
每次测试的时间限制
2秒
每次测试的内存限制
256兆字节
输入
标准输入
产量
标准输出

初学者程序员Xenia有一个序列a,由n个非负整数组成:1,  2,...,  nXenia目前正在研究位操作。为了更好地了解他们的工作,森雅决定计算一些数值v一个

即,计算值v需要几次迭代在第一次迭代中,捷尼亚写入一个新的序列一个  一个2,  一个  一个4,...,  一个Ñ  - 1   一个Ñ,由2个ñ  - 1元件。换句话说,她写下序列a的相邻元素的按位或在第二次迭代中,Xenia写入了按位独占在第一次迭代之后获得的序列的相邻元素的OR。在第三次迭代中,Xenia写入在第二次迭代之后获得的序列的相邻元素的按位或。等等; 按位异或运算和按位异或运算。最后,她得到一个由一个元素组成的序列,该元素是v

我们来看一个例子。假设序列a  =(1,2,3,4)然后让我们写下所有变换(1,2,3,4)  → (1    2 = 3,3    4 = 7)  →  (3  xor  7 = 4)结果是v  = 4

给你Xenia的初始序列。但是要计算给定序列的v太容易了,所以给你额外的m个查询。每个查询都是一对整数p,  b查询p,  b意味着您需要执行赋值p  =  b每次查询后,您需要为新序列a打印新值v

输入

第一行包含两个整数Ñ (1≤  Ñ  ≤17,1≤    ≤10 5下一行包含2个Ñ整数一个1,  一个2,...,  一个Ñ(0≤  一个  <2 30每个下一个的线包含查询。个行包含整数,   (1个≤    ≤2 Ñ,0≤  b  <2 30- 第 i个查询。

产量

打印m个整数 - 第i个整数表示i个查询后的序列a的v

例子
输入
复制
2 4 
1 6 3 5
1 4
3 4
1 2
1 2
产量
复制
1 
3
3
3
题意:输入总共2^n个数,然后让相邻的两个数进行|操作或者^操作,当是第一次的时候是|,第二次是^,这样交替进行
然后m次修改操作,这明显就是线段树的模板题,但是多了一个| ^ 的交替运算,可以判断这棵树的层数来判断是什么操作,然后发现n就是树的层数
或者用全局变量递归到最底层的时候实时更新也行

#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
struct sss
{
    int left;
    int right;
    int value;
}mp[ ((1 << 17) + 5)<<2];
int n,m;
int a[ (1 << 17) + 5];
void build(int cnt,int left,int right,int op)
{
    mp[cnt].left=left;
    mp[cnt].right=right;
    if(left==right)
    {
        mp[cnt].value=a[left];
        return ;
    }
    int mid=(left+right)/2;
    build(cnt<<1,left,mid,-op);
    build(cnt<<1|1,mid+1,right,-op);
    if(op==1)    mp[cnt].value=mp[cnt<<1].value|mp[cnt<<1|1].value;
    else    mp[cnt].value=mp[cnt<<1].value^mp[cnt<<1|1].value;
}
void updata(int cnt,int left,int right,int num,int value,int op)
{
    if(left==right)
    {
        mp[cnt].value=value;
        return;
    }
    int mid=(left+right)/2;
     if(num<=mid) updata(cnt<<1,left,mid,num,value,-op);
     else updata(cnt<<1|1,mid+1,right,num,value,-op);
    if(op==1)    mp[cnt].value=mp[cnt<<1].value|mp[cnt<<1|1].value;
    else    mp[cnt].value=mp[cnt<<1].value^mp[cnt<<1|1].value;
}
int main()
{
    scanf("%d%d",&n,&m);
    int t=(int)pow(2,n);
    for(int i=1;i<=t;i++)
    {
        scanf("%d",&a[i]);
    }
    int op;
    if(n&1) op=1;
    else op=-1;
    build(1,1,t,op);
    int x,y;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        updata(1,1,t,x,y,op);
        printf("%d\n",mp[1].value);
    }
}
B.队列
每次测试的时间限制
2秒
每次测试的内存限制
256兆字节
输入
标准输入
产量
标准输出

在机场中n个海象站在队列中。它们从队列的尾部开始编号:第1个海象站在队列的尾部,第n个海象站在队列的开始处。第海象有年龄等于一个

如果有一个年轻海象站在他的面前,即,如果存在这样的第海象变得不满Ĵ  <  Ĵ),即一个  >  一个Ĵi个海象不满他和前方最远的海象之间的海象数量相等,这比第i海象年轻也就是说,年轻的海象从他身上站得越远,这种不满就越强烈。

机场经理要求你为排队中n个海象中的每一个都算数

输入

第一行包含一个整数Ñ2≤  Ñ  ≤10 5) -在队列海象的数量。第二行包含整数1≤  一个  ≤10 9)。

请注意,有些海象可能具有相同的年龄,但是由于出现不满情绪,靠近队头的海象需要比其他海象年轻

产量

打印n个数字:如果第i个海象对所有事情都满意,打印“-1”(不含引号)。否则,就打印了个海象的不悦:那个站在他并从他最远的年轻海象之间的其他海象的数量。

例子
输入
复制
6 
10 8 5 3 50 45
产量
复制
2 1 0 -1 0 -1 
输入
复制
7 
10 4 6 3 2 8 15
产量
复制
4 2 1 0 -1 -1 -1 
输入
复制
5 
10 3 1 10 11
产量
复制
1 0 -1 -1 -1 

 题意:每个海象从后面选最远的一个比他小的数,算中间隔的个数

用线段树维护一段区间的最小值,然后每个点查询完之后,把当前点的值置为MAX,这样下次搜寻的时候便不会搜到在他前面的数,算中间的数字个数就是算下标相减再加1、

搜索的时候因为想尽量远,所以优先搜右子树是否有比当前数小的数,有则说明右边搜索中间数更加多

#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAX 9999999999;
using namespace std;
struct sss
{
    int left;
    int right;
    int value;
}mp[100001<<4];
int a[100001];
void build(int cnt,int left,int right)
{
    mp[cnt].left=left;
    mp[cnt].right=right;
    if(left==right)
    {
        mp[cnt].value=a[left];
        return;
    }
    int mid=(left+right)/2;
    build(cnt<<1,left,mid);
    build(cnt<<1|1,mid+1,right);
    mp[cnt].value=min(mp[cnt<<1].value,mp[cnt<<1|1].value);
}
void find(int cnt,int i,int num)
{
    if(mp[cnt].left==mp[cnt].right)
    {
        printf("%d ",mp[cnt].left-i-1);
        return;
    }
    if(mp[cnt<<1|1].value<num)
    {
        find(cnt<<1|1,i,num);
    }
    else 
    {
        find(cnt<<1,i,num);
    }
}
void updata(int cnt,int num)
{
    if(mp[cnt].left==mp[cnt].right)
    {
        mp[cnt].value=MAX;
        return;
    }
    int mid=(mp[cnt].left+mp[cnt].right)/2;
    if(num<=mid) updata(cnt<<1,num);
    else updata(cnt<<1|1,num);
    mp[cnt].value=min(mp[cnt<<1].value,mp[cnt<<1|1].value);
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    build(1,1,n);
    for(int i=1;i<=n;i++)
    {
        if(mp[1].value>=a[i]) printf("-1 ");
        else find(1,i,a[i]);
        updata(1,i);
    }
}

总结:线段树是一个灵活多变的一个玩意儿,但是实际上每次存的时候只有那么几种,所以重要的还是题目理清,灵活应变的去思考,以后还是要多做思维题

 

猜你喜欢

转载自www.cnblogs.com/Lis-/p/9151973.html