線段樹,樹狀數組(單點操作)
輸入
在輸入中,第一行包含一個整數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和位操作 每次測試的時間限制
初學者程序員Xenia有一個序列a,由2 n個非負整數組成:a 1,? a 2,...,? a 2 n。Xenia目前正在研究位操作。為了更好地了解他們的工作,森雅決定計算一些數值v的一個。
即,計算值v需要幾次叠代。在第一次叠代中,捷尼亞寫入一個新的序列一個1 或 一個2,? 一個3 或 一個4,...,? 一個2 ? ?- 1 或 一個2 ?,由2個? ?- 1元件。換句話說,她寫下序列a的相鄰元素的按位或。在第二次叠代中,Xenia寫入了按位獨占在第一次叠代之後獲得的序列的相鄰元素的OR。在第三次叠代中,Xenia寫入在第二次叠代之後獲得的序列的相鄰元素的按位或。
我們來看一個例子。假設序列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); } }
總結:線段樹是一個靈活多變的一個玩意兒,但是實際上每次存的時候只有那麽幾種,所以重要的還是題目理清,靈活應變的去思考,以後還是要多做思維題
線段樹,樹狀數組(單點操作)