二维偏序 tree
tree(二維偏序)
最近接觸到一些偏序的東西。
傳統(tǒng)線段樹非葉子節(jié)點(diǎn)的劃分點(diǎn)mid=(l+r)/2,但小R線段樹mid是自己定的。但滿足l<=mid<r,其余條件同原來線段樹。那么不難發(fā)現(xiàn)如下性質(zhì):1.該線段樹的節(jié)點(diǎn)個(gè)數(shù)依然為2N-1.2.該線段樹深度可能會(huì)超過O(logn)。3.該線段樹區(qū)間定位所包含的線段樹節(jié)點(diǎn)個(gè)數(shù)可能超過O(logn)。但區(qū)間定位的結(jié)果依然是唯一的。小R給你這樣一個(gè)小R線段樹,每次詢問給定區(qū)間的區(qū)間定位個(gè)數(shù)。
? 這道題n和詢問個(gè)數(shù)都到了1e5,所以考慮nlogn的做法。
? 我們發(fā)現(xiàn)區(qū)間定位個(gè)數(shù)(答案)和完全被該區(qū)間包含的節(jié)點(diǎn)個(gè)數(shù)所相關(guān)。具體性質(zhì)如下:區(qū)間定位個(gè)數(shù)(答案) = 2 * 區(qū)間長(zhǎng)度 - 完全被該區(qū)間包含的節(jié)點(diǎn)個(gè)數(shù)。不難發(fā)現(xiàn)完全被該區(qū)間包含的節(jié)點(diǎn)個(gè)可以看作一個(gè)森林,而區(qū)間定位出的那些線段節(jié)點(diǎn)可以看作這些森林當(dāng)中數(shù)的根。由于一個(gè)長(zhǎng)度為l的根的樹的節(jié)點(diǎn)個(gè)數(shù)為 2 ? l ? 1,那么這個(gè)森林當(dāng)中每有一棵樹,就會(huì)對(duì)“2 * 區(qū)間長(zhǎng)度”這個(gè)總和上減去一,故滿足性質(zhì)。那么我們將問題轉(zhuǎn)化為求完全被該區(qū)間包含的節(jié)點(diǎn)個(gè)數(shù)。
? 那么現(xiàn)在問題就是在所有的區(qū)間中,找到[l,r]所完全包含的區(qū)間。這是個(gè)二維偏序的問題。個(gè)人理解所謂二維偏序就是詢問許多向量中,比詢問的向量小的向量。由于這是偏序關(guān)系,所以不是任意兩個(gè)向量都可以比較大小。這里的定義是如果一個(gè)區(qū)間l,r,l<=l2,并且r>=r2,那么l,r小于l2,r2。具體做法就是一維排序,然后i從大到小(從小到大也可以,只不過這里dfs出來順序是從小到大的,那么就從大到小掃一遍),對(duì)于左端點(diǎn)在當(dāng)前詢問左端點(diǎn)右側(cè)的區(qū)間,把其右側(cè)端點(diǎn)在樹狀數(shù)組里mark一下。然后查詢的時(shí)候,只查詢1到r被mark的值。這樣由于只統(tǒng)計(jì)了左端點(diǎn)在l右側(cè)的區(qū)間,并且區(qū)間的右端點(diǎn)被限制在1到r以內(nèi),所以所有區(qū)間都會(huì)被掃到。
? 感覺這種方法真奇妙。。
#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){//如果這個(gè)區(qū)間,它的左端點(diǎn)在查詢的左端點(diǎn)右邊//說明它對(duì)查詢的左端點(diǎn)是有效果的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; }轉(zhuǎn)載于:https://www.cnblogs.com/MyNameIsPc/p/7657792.html
總結(jié)
- 上一篇: linux下的setenv使用
- 下一篇: jQ属性操作