主席树几道题目

来源:互联网 发布:贾米森数据 编辑:程序博客网 时间:2024/05/18 03:35


主要参考cxlove博客kuangbin博客

感觉主席树和划分树有类似之处,分治的思想


Poj2104 主席树静态区间第k值

以每个位置为起点,建立一棵主席树,保存后缀区间的情况。

每个节点保存的是该后缀在相应区间数的个数

const int MAXN = 100010;const int MOD = 1000000;int a[MAXN], t[MAXN];int T[MAXN * 30], lson[MAXN * 30],rson[MAXN * 30], c[MAXN * 30];int n, sz, q;int tot;void init_hash(){   FE(i, 1, n) t[i] = a[i];   sort(t + 1, t + 1 + n);   sz = unique(t + 1, t + 1 + n) - t - 1;}int build(int l, int r){   int rt = tot++;   c[rt] = 0;   if (l != r)    {       int m = (l + r) >> 1;       lson[rt] = build(l, m);       rson[rt] = build(m + 1, r);    }   return rt;} int hash(int x){    returnlower_bound(t + 1, t + 1 + n, x) - t;} int update(int rt, int pos, int val){   int new_rt = tot++;   int tmp = new_rt;   c[new_rt] = c[rt] + val;   int l = 1, r = sz;   while (l < r)    {       int m = (l + r) >> 1;       if (pos <= m)       {           lson[new_rt] = tot++; rson[new_rt] = rson[rt];           new_rt = lson[new_rt]; rt = lson[rt];           r = m;       }       else       {           rson[new_rt] = tot++; lson[new_rt] = lson[rt];           new_rt = rson[new_rt]; rt = rson[rt];           l = m + 1;       }       c[new_rt] = c[rt] + val;    }   return tmp;} int query(int L, int  R, int k){   int l = 1, r = sz;   while (l < r)    {       int m = (l + r) >> 1;       if (c[lson[L]] - c[lson[R]] >= k)       {           L = lson[L]; R = lson[R];           r = m;       }       else       {           k -= c[lson[L]] - c[lson[R]];///!!!           L = rson[L]; R = rson[R];           l = m + 1;       }    }   return l;} int main(){   int x, y, z;   while (cin >> n >> q)    {       tot = 0;       FE(i, 1, n) RI(a[i]);       init_hash();       T[n + 1] = build(1, sz);///!!!       FED(i, n, 1)       {           int pos = hash(a[i]);           T[i] = update(T[i + 1], pos, 1);       }       while (q--)       {           RIII(x, y, z);           printf("%d\n", t[query(T[x], T[y + 1], z)]);       }    }}


spoj EQUERY 静态区间,区间中不同值的数的个数

对于每一个起始位置,都建立一棵主席树

从后往前更新,更新的时候就是在next位置先减掉,然后更新在当前位置。

主席树又称为可持久化线段树,其本质上是保存了所有的历史信息。

也可以理解为多棵线段树,在更新的时候,充分共同利用历史信息

对于点更新来说,更新的总是某一棵子树,另外一棵子树,则指向原来的结点即可。

可是即使如此,主席树还是需要大量的空间。

const int MAXN = 100010;const int MOD = 1000000; int a[MAXN], t[MAXN];int T[MAXN * 60], lson[MAXN * 60],rson[MAXN * 60], c[MAXN * 60];int n, q;int tot;map<int, int> mp; int build(int l, int r){   int rt = tot++;   c[rt] = 0;   if (l != r)    {       int m = (l + r) >> 1;       lson[rt] = build(l, m);        rson[rt] = build(m + 1, r);    }   return rt;} int update(int rt, int pos, int val){   int new_rt = tot++;   int tmp = new_rt;   c[new_rt] = c[rt] + val;   int l = 1, r = n;   while (l < r)    {       int m = (l + r) >> 1;       if (pos <= m)       {           lson[new_rt] = tot++; rson[new_rt] = rson[rt];           new_rt = lson[new_rt]; rt = lson[rt];           r = m;       }       else       {           rson[new_rt] = tot++; lson[new_rt] = lson[rt];           new_rt = rson[new_rt]; rt = rson[rt];           l = m + 1;       }       c[new_rt] = c[rt] + val;    }   return tmp;} int query(int rt, int pos){   int ans = 0;   int l = 1, r = n;   while (l < r)///(pos < r)    {       int m = (l + r) >> 1;        if (pos <= m)       {           rt = lson[rt];           r = m;       }       else       {           ans += c[lson[rt]];           rt = rson[rt];           l = m + 1;       }    }   return ans + c[rt];} int main(){   int x, y, z;   while (cin >> n)    {       tot = 0;       FE(i, 1, n) RI(a[i]);       mp.clear();       T[n + 1] = build(1, n);       FED(i, n, 1)       {           if (mp.find(a[i]) == mp.end())           {                T[i] = update(T[i + 1], i, 1);           }           else           {                int tmp = update(T[i + 1],mp[a[i]], -1);                T[i] = update(tmp, i, 1);           }           mp[a[i]] = i;       }       cin >> q;       while (q--)        {           RII(x, y);           printf("%d\n", query(T[x], y));       }    }} 

zoj 2112 Dynamic Rankings 动态第k值,树状数组套线段树


、、、