1. 程式人生 > >線段樹,樹狀數組(單點操作)

線段樹,樹狀數組(單點操作)

容易 當前 AI CA src http 開始 搜索 異或運算

考慮一個無限完整的二叉搜索樹(參見下圖),節點中的數字是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,由2 n個非負整數組成:a 1,? a 2,...,? a 2 nXenia目前正在研究位操作。為了更好地了解他們的工作,森雅決定計算一些數值v一個

即,計算值v需要幾次叠代在第一次叠代中,捷尼亞寫入一個新的序列一個1 一個2,? 一個3 一個4,...,? 一個2 ? ?- 1 一個2 ?,由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意味著您需要執行賦值a p ?=? b每次查詢後,您需要為新序列a打印新值v

輸入

第一行包含兩個整數? (1≤? ? ?≤17,1≤? ?≤10 5下一行包含2個?整數一個1,? 一個2,...,? 一個2 ?(0≤? 一個 ?<2 30每個下一個的線包含查詢。個行包含整數p ,? b (1個≤? p ?≤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);
    }
}

總結:線段樹是一個靈活多變的一個玩意兒,但是實際上每次存的時候只有那麽幾種,所以重要的還是題目理清,靈活應變的去思考,以後還是要多做思維題

 

線段樹,樹狀數組(單點操作)