二维偏序 tree
tree(二維偏序)
最近接觸到一些偏序的東西。
傳統線段樹非葉子節點的劃分點mid=(l+r)/2,但小R線段樹mid是自己定的。但滿足l<=mid<r,其余條件同原來線段樹。那么不難發現如下性質:1.該線段樹的節點個數依然為2N-1.2.該線段樹深度可能會超過O(logn)。3.該線段樹區間定位所包含的線段樹節點個數可能超過O(logn)。但區間定位的結果依然是唯一的。小R給你這樣一個小R線段樹,每次詢問給定區間的區間定位個數。
? 這道題n和詢問個數都到了1e5,所以考慮nlogn的做法。
? 我們發現區間定位個數(答案)和完全被該區間包含的節點個數所相關。具體性質如下:區間定位個數(答案) = 2 * 區間長度 - 完全被該區間包含的節點個數。不難發現完全被該區間包含的節點個可以看作一個森林,而區間定位出的那些線段節點可以看作這些森林當中數的根。由于一個長度為l的根的樹的節點個數為 2 ? l ? 1,那么這個森林當中每有一棵樹,就會對“2 * 區間長度”這個總和上減去一,故滿足性質。那么我們將問題轉化為求完全被該區間包含的節點個數。
? 那么現在問題就是在所有的區間中,找到[l,r]所完全包含的區間。這是個二維偏序的問題。個人理解所謂二維偏序就是詢問許多向量中,比詢問的向量小的向量。由于這是偏序關系,所以不是任意兩個向量都可以比較大小。這里的定義是如果一個區間l,r,l<=l2,并且r>=r2,那么l,r小于l2,r2。具體做法就是一維排序,然后i從大到小(從小到大也可以,只不過這里dfs出來順序是從小到大的,那么就從大到小掃一遍),對于左端點在當前詢問左端點右側的區間,把其右側端點在樹狀數組里mark一下。然后查詢的時候,只查詢1到r被mark的值。這樣由于只統計了左端點在l右側的區間,并且區間的右端點被限制在1到r以內,所以所有區間都會被掃到。
? 感覺這種方法真奇妙。。
#include <cstdio> #include <algorithm> using namespace std;const int maxn=4e5+5; int n, m, cntseg, cntline, cntq, beg; int atree[maxn];struct Node{int lson, rson, mid; }node[maxn];struct Line{int l, r, id, ans;void set(const int &x, const int &y){ l=x, r=y; } }line[maxn], queries[maxn]; bool cmp1(const Line &x, const Line &y){return x.l==y.l?x.r<y.r:x.l<y.l; } bool cmp2(const Line &x, const Line &y){return x.id<y.id; }void build(int &now, int l, int r){line[cntline++].set(l, r);if (l==r) return;if (!now) now=++cntseg;scanf("%d", &node[now].mid);build(node[now].lson, l, node[now].mid);build(node[now].rson, node[now].mid+1, r); }inline int lowbit(int x){ return x&(-x); }void modify(int now){while (now<=n){++atree[now];now+=lowbit(now);} }int query(int now){int re=0;while (now){re+=atree[now];now-=lowbit(now);} return re; }int main(){scanf("%d%d", &n, &m);build(beg, 1, n);int l, r;for (int i=0; i<m; ++i){scanf("%d%d", &l, &r);queries[cntq].set(l, r);queries[cntq++].id=i;}sort(queries, queries+m, cmp1);int j=cntline-1;for (int i=m-1; i>=0; --i){//如果這個區間,它的左端點在查詢的左端點右邊//說明它對查詢的左端點是有效果的while (line[j].l>=queries[i].l){modify(line[j].r); --j;}queries[i].ans=2*(queries[i].r-queries[i].l+1)-query(queries[i].r);}sort(queries, queries+m, cmp2);for (int i=0; i<m; ++i)printf("%d\n", queries[i].ans);return 0; }轉載于:https://www.cnblogs.com/MyNameIsPc/p/7657792.html
總結
- 上一篇: linux下的setenv使用
- 下一篇: JavaScript创建Element元