1. 程式人生 > >Little Elephant and Array 線段樹

Little Elephant and Array 線段樹

為什麽 csdn 就是 位置 ont -s include cin lin

題目:http://codeforces.com/problemset/problem/220/B

題意

給定一組數據,多次詢問區間內某數字出現次數與該數字數值相同的數的個數

思路

一看到區間查詢,就會想到線段樹,有木有!

單點或區間的修改、查詢等可是線段樹的強項嘞√

而我們今天的線段樹類型為: 離線處理、區間更新、單點查詢

給定一個數字序列,線段樹每個節點所要記錄的狀態是 :從此處以及到正在處理的元素處滿足題意的ans

上面一句話什麽意思嘞

我們做的是 邊建樹,邊查詢,當對應狀態的線段樹建立完成,即可得到答案。

比如 一段序列 : 8 3 4 4 1 3 3 2 2

當我們處理到第7個數的時候,狀態分別為 2 2 1 1 1 0 0

以上是線段樹的屬性介紹,下面就是具體的操作流程

我們需要知道不同數字所出現的所有的位置,用一個二維空間pos存儲該信息,比如上面pos[3]存儲的數據即為 2、6、7

首先,將所有的詢問區間按照右端點進行非遞減排序

我們從序列的第一數字開始,建樹。

當num數字第 t 次出現時候,如果,t >= num的時候,我們將【1,num第一次出現的位置】的值+1,

如果,t > num 的時候,我們將【1,num第一次出現的位置】的值-1

以此來保證線段樹每個節點所要記錄的狀態

如此做,當處理到第i個數字的時候,查看是否到達當前正在處理的區間的右端點,

如果到達,那麽,當前處理區間的左端點的節點狀態即為所求

為什麽呢,因為我們是一個一個將序列中的元素放入樹中的,而我們的詢問區間是按照右端點非遞減排序的,那麽就保證了,處理到第i個序列元素的時候,到達了詢問區間右端點,而根據樹節點所記錄的狀態可知,即為答案,而此時的狀態,並沒有把i處之後的序列元素作考慮,所以,按照區間右端點遞增,且序列元素逐一入樹,邊建樹邊查詢,樹節點所記錄的狀態定為答案。

為了方便,我們將每種數字第一次出現的位置均賦值為0

代碼如下:

#include<iostream>
#include<algorithm>  
#include<vector>  
using
namespace std; const int MAXN = 100005; int N, M, list[MAXN], s[MAXN], t[MAXN], rank_[MAXN], result[MAXN]; int Tr[MAXN << 2], mark[MAXN << 2]; vector<int> pos[MAXN]; inline bool cmp(const int a, const int b) { return t[a] < t[b]; } void PushDown(int idx); //向下更新 沿para向下更新子樹 void Update(int idx, int L, int R, int l, int r, int c); //區間更新 para1:當前結點的樹下標 int Query(int idx, int L, int R, int x); //單點詢問 int main() { cin >> N >> M; for (int i = 1; i <= N; i++) { cin >> list[i]; if (list[i] <= N && !pos[list[i]].size()) pos[list[i]].push_back(0); } for (int i = 0; i < M; i++) { cin >> s[i] >> t[i]; rank_[i] = i; } sort(rank_, rank_ + M, cmp); for (int i = 1, j = 0; i <= N && j < M; i++) { if (i == 7) int s = 0; if (list[i] <= N) { pos[list[i]].push_back(i); if (pos[list[i]].size() > list[i]) Update(1, 1, N, pos[list[i]][pos[list[i]].size() - list[i] - 1] + 1, pos[list[i]][pos[list[i]].size() - list[i]], 1); if (pos[list[i]].size() > list[i] + 1) Update(1, 1, N, pos[list[i]][pos[list[i]].size() - list[i] - 2] + 1, pos[list[i]][pos[list[i]].size() - list[i] - 1], -1); } for (; t[rank_[j]] == i && j < M; j++) result[rank_[j]] = Query(1, 1, N, s[rank_[j]]); } for (int i = 0; i < M; i++) printf("%d\n", result[i]); return 0; } void PushDown(int idx) { int left = idx << 1, right = (idx << 1) ^ 1; Tr[left] += mark[idx]; mark[left] += mark[idx]; Tr[right] += mark[idx]; mark[right] += mark[idx]; mark[idx] = 0; } void Update(int idx, int L, int R, int l, int r, int c) { if (l <= L && R <= r) { Tr[idx] += c; mark[idx] += c; return; } if (mark[idx]) PushDown(idx); int mid = (L + R) >> 1, left = idx << 1, right = (idx << 1) ^ 1; if (l <= mid) Update(left, L, mid, l, r, c); if (mid < r) Update(right, mid + 1, R, l, r, c); } int Query(int idx, int L, int R, int x) { if (x == L & R == x) return Tr[idx]; if (mark[idx]) PushDown(idx); int mid = (L + R) >> 1, left = idx << 1, right = (idx << 1) ^ 1; if (x <= mid) return Query(left, L, mid, x); else return Query(right, mid + 1, R, x); }

當然,作為線段樹的好朋友,樹狀數組當然也可以做嘍

見:https://blog.csdn.net/u011026968/article/details/38589907

感謝您的閱讀,生活愉快~

Little Elephant and Array 線段樹