zoukankan      html  css  js  c++  java
  • 初探主席树1

    主席树是函数式线段树的前缀和或树状数组套函数式线段树。一般来说的主席树是树状数组套函数式线段树……——VFleaKing

    其实关于这个东西Seter已经讲的很清楚了。我就讲讲具体实现方法吧(非递归)。

    1. 函数式线段树的前缀和

    先来看一道例题:poj2104

    建树

    我们其实是要建n棵线段树,我们用root[i]代表第I棵线段树的根。从1到n建过去。

    void update(int old,int l,int r,const int num,int &root)
    { root = Vc + 1;
    for (int mid;l != r;) {
    mid = (l + r) / 2;
    tree[++Vc] = tree[old]; ++tree[Vc].sum;
    if (num > mid) old = tree[old].rc,tree[Vc].rc = Vc + 1,l = mid + 1;
    else old = tree[old].lc,tree[Vc].lc = Vc + 1,r = mid;
    }
    tree[++Vc] = node(0,0,tree[old].sum + 1);
    }

     这个Update操作就是建第i棵线段树的操作,old为i-1棵线段树的根, l为线段树的左边界, r为右边界, num为要插入的数, fa为一个指针,为这棵线段树当前点的父节点,这样方便更新结点的儿子信息。 ,root为根。具体见代码。

     可以这样调用(没有离散化等优化的情况下):

    root[0] = ++Vc;

    for (int i = 1; i <= n; ++i)

      update(root[i - 1],1,max_ai,a[i],root[i]);

     询问

    询问其实跟普通线段树一样,用root[r]的值减掉root[l - 1]的值就可以得到a[l]...a[r]建出来的线段树的值了

    int query(int l,int r,int rank)
    {
    	int lb = 1,ub = tmp[0],t;
    	for (; lb != ub; ) {
    		int mid = (lb + ub) / 2;
    		if ((t = tree[tree[r].lc].sum - tree[tree[l].lc].sum) < rank) {
    			rank -= t,lb = mid + 1;
    			l = tree[l].rc;
    			r = tree[r].rc;
    		}else {
    			ub = mid;
    			l = tree[l].lc;
    			r = tree[r].lc;
    		}
    	}
    	return tmp[lb];
    }

    离散化

    我直接用stl的函数了

    std::sort(tmp + 1,tmp + 1 + n);

    tmp[0] = std::unique(tmp + 1,tmp + 1 + n) - tmp - 1;

    用的时候像这样

    std::lower_bound(tmp + 1,tmp + 1 + tmp[0],seq[boundary]) - tmp

    时间复杂度是O(N*logN),空间复杂度O(N*logN)

    代码:

    #include <cstdio>
    #include <algorithm>
    const int N = 100000 + 9;
    struct node
    {
    	int lc,rc,sum;
    	node(const int a = 0,const int b = 0,const int c = 0):
    		lc(a),rc(b),sum(c){}
    }tree[N*18];
    int tmp[N],n,m,root[N],Vc,a[N],seq[N];
    void update(int old,int l,int r,const int num,int &root)
    {
    	root = Vc + 1;
    	for (int mid;l != r;) {
    		mid = (l + r) / 2;
    		tree[++Vc] = tree[old]; ++tree[Vc].sum;
    		if (num > mid) old = tree[old].rc,tree[Vc].rc = Vc + 1,l = mid + 1;
    		else old = tree[old].lc,tree[Vc].lc = Vc + 1,r = mid;
    	}
    	tree[++Vc] = node(0,0,tree[old].sum + 1);
    }
    int query(int l,int r,int rank)
    {
    	int lb = 1,ub = tmp[0],t;
    	for (; lb != ub; ) {
    		int mid = (lb + ub) / 2;
    		if ((t = tree[tree[r].lc].sum - tree[tree[l].lc].sum) < rank) {
    			rank -= t,lb = mid + 1;
    			l = tree[l].rc;
    			r = tree[r].rc;
    		}else {
    			ub = mid;
    			l = tree[l].lc;
    			r = tree[r].lc;
    		}
    	}
    	return tmp[lb];
    }
    int main()
    {
    	#ifndef ONLINE_JUDGE
    	freopen("2104.in","r",stdin);
    	freopen("2104.out","w",stdout);
    	#endif
    	scanf("%d%d",&n,&m);
    	for (int i = 1; i <= n; ++i) {
    		scanf("%d",seq + i);
    		tmp[i] = seq[i];
    	}
    	std::sort(tmp + 1,tmp + 1 + n);
    	tmp[0] = std::unique(tmp + 1,tmp + 1 + n) - tmp - 1;
    	root[0] = ++Vc; /*tree[Vc].lb = 1; tree[Vc].rb = tmp[0]*/;
    	for (int x,y,z,boundary = 1; m--;) {
    		scanf("%d%d%d",&x,&y,&z);
    		if (boundary <= y) 
    			for (; boundary <= y; ++boundary) 
    				update(root[boundary - 1],1,n,a[boundary] ? a[boundary] : a[boundary] = 
    				std::lower_bound(tmp + 1,tmp + 1 + tmp[0],seq[boundary]) - tmp,root[boundary]);
    		printf("%d
    ",query(root[x - 1],root[y],z));
    	}
    }
    

      

    还有一道题,也是借鉴了函数式编程的思想:bzoj3261: 最大异或和

    也是运用了函数式编程的思想。这个我将另写题解。

  • 相关阅读:
    二维动规思想,j 具有明显枚举特征
    二分法题目总结
    最大(小)值最小(大)化 (二分法变形)
    C/ C++ 输入输出流
    正序扫描字符串问题
    React(基础一)_react中的三大属性
    找位置
    STL vector
    STL stack
    打印日期
  • 原文地址:https://www.cnblogs.com/lazycal/p/3251740.html
Copyright © 2011-2022 走看看