ZOJ 2112 Dynamic Rankings(主席树套树状数组+静态主席树)

题意:给定一个区间,求这个区间第k小的数,支持单点修改。

思路:主席树真是个神奇的东西.........速度很快但是也有一个问题就是占用内存的很大,一般来说支持单点修改的主席树套树状数组空间复杂度为O(n*logn*logn), 如果查询较少的话,可以初始的时候用一颗静态主席树,这样空间复杂度可以降为O(n*logn+q*logn*logn),勉强可以过zoj这道题。

这道题看了好久好久才懂...网上题解一般就几句话.......下面是自己的一些理解。

首先静态主席树这个东西其实比较好懂,就是对于每个前缀[1,1],[1,2]....[1,n]都建一颗线段树,将数据离散化后,在线段树中统计区间内所有值出现的次数,每一个线段树都是相同的形态,那么这样我们得到一个很好的性质,这些线段树可以“相减”,假设当前查询的是区间[l,r]内第k大的值,那么我们用前缀[1, r]这个线段树和前缀[1, l-1]这颗线段树通过相减加上二分就可以找到答案。由于相邻两颗线段树最多只有logn个节点不同,我们没有必要完全新建一颗线段树,只需要把相同的结点用指针指一下就行,然后新建logn个结点,这样一来时空复杂度为n*logn。

对于动态查询的主席树,如果修改一个点的值,我们肯定不能修改所有包含这个点的线段树,时间复杂度无法承受。

那么我们就可以套上树状数组,个人感觉这里比较难懂。

树状数组的每个节点都是一颗线段树,但这棵线段树不再保存每个前缀的信息了,而是由树状数组的sum函数计算出这个前缀的信息,那么显而易见这棵线段树保存的是辅助数组S的值,即S=A[i-lowbit+1]+...+A[i],其中A[i]表示值为i的元素出现的次数。

那么对于每次修改,我们要修改树状数组上的logn棵树,对于每棵树,我们要修改logn个结点,所以时空复杂度为

O((n+q)*logn*logn),由于这道题n比较大,查询次数q比较小,所以我们可以初始时建立一颗静态的主席树,树状数组只保存每次修改的信息,那么时空复杂度降为了O(n*logn+q*logn*logn)。

代码参考自:http://www.cnblogs.com/kuangbin/p/3308118.html

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define eps 1e-6
#define LL long long
#define pii (pair)
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

//zoj 2104 动态主席树修改+静态主席树 
const int maxn = 60000+1000;
const int M = 2400000;
int n, q, m, tot;
int a[maxn], t[maxn];
int T[maxn], lson[M], rson[M], c[M];
int S[maxn];
struct Query {
	int kind;
	int l, r, k;
} query[10010];

void Init_hash(int k) {
	sort(t, t+k);
	m = unique(t, t+k) - t;
}

int Hash(int x) {
	return lower_bound(t, t+m, x) - t;
}

int build(int l, int r) {
	int root = tot++;
	c[root] = 0;
	if(l != r) {
		int mid = (l+r) >> 1;
		lson[root] = build(l, mid);
		rson[root] = build(mid+1, r);
	}
	return root;
}

int Insert(int root, int pos, int val) {
	int newroot = tot++, tmp = newroot;
	int l = 0, r = m-1;
	c[newroot] = c[root] + val;
    while(l < r) {
        int mid = (l+r)>>1;
        if(pos <= mid) {
            lson[newroot] = tot++; rson[newroot] = rson[root];
            newroot = lson[newroot]; root = lson[root];
            r = mid;
        }
        else {
            rson[newroot] = tot++; lson[newroot] = lson[root];
            newroot = rson[newroot]; root = rson[root];
            l = mid+1;
        }
        c[newroot] = c[root] + val;
    }
	return tmp;
}

int lowbit(int x) {
	return x&(-x);
}
int use[maxn];
void add(int x, int pos, int d) {
	while(x <= n) {
		S[x] = Insert(S[x], pos, d);
		x += lowbit(x);
	}
}
int Sum(int x) {
	int ret = 0;
	while(x > 0) {
		ret += c[lson[use[x]]];
		x -= lowbit(x);
	}
	return ret;
}
int Query(int left, int right, int k) {
	int left_root = T[left-1], right_root = T[right];
	int l = 0, r = m-1;
	for(int i = left-1; i; i -= lowbit(i)) use[i] = S[i];
	for(int i = right; i; i -= lowbit(i)) use[i] = S[i];
	while(l < r) {
		int mid = (l+r) >> 1;
		int tmp = Sum(right) - Sum(left-1) + c[lson[right_root]] - c[lson[left_root]];
		if(tmp >= k) {
			r = mid;
			for(int i = left-1; i; i -= lowbit(i)) use[i] = lson[use[i]];
			for(int i = right; i; i -= lowbit(i)) use[i] = lson[use[i]];
			left_root = lson[left_root];
			right_root = lson[right_root];
		}
		else {
			l = mid + 1;
			k -= tmp;
			for(int i = left-1; i; i -= lowbit(i)) use[i] = rson[use[i]];
			for(int i = right; i; i -= lowbit(i)) use[i] = rson[use[i]];
			left_root = rson[left_root];
			right_root = rson[right_root];
		}		
	}
	return l;
}

int main() {
    //freopen("input.txt", "r", stdin);
    int Tcase; cin >> Tcase; 
	while(Tcase--) {
		scanf("%d%d", &n, &q);
		tot = 0;
		m = 0;
		for(int i = 1; i <= n; i++) {
			scanf("%d", &a[i]);
			t[m++] = a[i];
		}
		char op[10];
		for(int i = 0;i < q;i++) {
            scanf("%s",op);
           	if(op[0] == 'Q') {
           		query[i].kind = 0;
               	scanf("%d%d%d",&query[i].l,&query[i].r,&query[i].k);
           	}
           	else {
               	query[i].kind = 1;
               	scanf("%d%d", &query[i].l, &query[i].r);
               	t[m++] = query[i].r;
           	}
       	}
		Init_hash(m);
		T[0] = build(0, m-1);
		for(int i = 1; i <= n; i++) T[i] = Insert(T[i-1], Hash(a[i]), 1);
		for(int i = 1; i <= n; i++) S[i] = T[0];
		for(int i = 0; i < q; i++) {
            if(query[i].kind == 0) printf("%d\n",t[Query(query[i].l,query[i].r,query[i].k)]);
            else {
                add(query[i].l, Hash(a[query[i].l]), -1);
                add(query[i].l, Hash(query[i].r), 1);
                a[query[i].l] = query[i].r;
            }
        }		
    }
    return 0;
}



你可能感兴趣的:(程序设计竞赛)