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<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#include<ctime>
#define eps 1e-6
#define LL long long
#define pii (pair<int, int>)
//#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;
}



版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-13 13:00:53

ZOJ 2112 Dynamic Rankings(主席树套树状数组+静态主席树)的相关文章

zoj 2112 Dynamic Rankings 带修改区间第K大 动态主席树

pass 首先,个人觉得把这个数据结构理解成树状数组套主席树是十分不严谨的.主席树的本质是可持久化权值线段树与前缀和思想的结合.而动态主席树是可持久化权值线段树与树状数组思想的结合.并非树套树般的泾渭分明的叠加. 其次,大概讲下对动态主席树的理解.我们静态主席树中,第i个版本维护的是[1,i]的权值线段树,我们利用前缀和的思想,通过y的版本-x的版本得到[x,y]的权值线段树,从而剥离出一颗对应区间的权值线段树.我们考虑在这个情况下,如果需要修改第a[i]的值,那么从i,i+1,i+2.....

ZOJ 2112 Dynamic Rankings(主席树の动态kth)

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2112 The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied with the query like to simply find the k-th smallest number of the given N numbers. T

zoj 2112 Dynamic Rankings(主席树&amp;动态第k大)

Dynamic Rankings Time Limit: 10 Seconds      Memory Limit: 32768 KB The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied with the query like to simply find the k-th smallest number of the given N numbers. They

zoj 2112 Dynamic Rankings(树状数组套主席树)

题意:对于一段区间,每次求[l,r]的第k大,存在单点修改操作: 思路: 学习主席树参考: http://blog.csdn.net/wjf_wzzc/article/details/24560117(各种形式) http://blog.csdn.net/bossup/article/details/31921235(推荐) http://blog.csdn.net/xiaofengcanyuexj/article/details/25553521?utm_source=tuicool(图解)

ZOJ -2112 Dynamic Rankings 主席树 待修改的区间第K大

Dynamic Rankings 带修改的区间第K大其实就是先和静态区间第K大的操作一样.先建立一颗主席树, 然后再在树状数组的每一个节点开线段树(其实也是主席树,共用节点), 每次修改的时候都按照树状数组的方式去修改,并且修改那些地方.查询的时候就是查询原主席树+树状数组的值. 代码: 1 #include<bits/stdc++.h> 2 using namespace std; 3 #define Fopen freopen("_in.txt","r&quo

zoj 2112 Dynamic Rankings 动态第k大 线段树套Treap

Dynamic Rankings Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2112 Description The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied with the query l

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

The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied with the query like to simply find the k-th smallest number of the given N numbers. They have developed a more powerful system such that for N numbers a[1],

ZOJ 2112 Dynamic Rankings BIT套ChairTree

劳资就是想单纯的测个模板,怎么就特喵的这么难. 首先说一下自己对主席树的理解. 作用:确定区间[L,R]内的第K大数,或者区间内有多少个不同个元素. 主席树的另一个名字叫做函数式线段树,然后我查了一下函数式的特点,其中之一就是不修改状态. 从主席树上来看这个特点就是:在向主席树插入第 i 个元素之后,我们仍能查询到没插入 i 之前的状态. 主席树的构造: 对于有n个元素,s个不同元素的区间,对应的主席树CT 是由n 棵线段树组成的 ,对于第 i 棵线段树记录[1,i]区间内s个元素出现的次数.

ZOJ 2112 Dynamic Rankings(带修改的区间第K大,分块+二分搜索+二分答案)

Dynamic Rankings Time Limit: 10 Seconds      Memory Limit: 32768 KB The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied with the query like to simply find the k-th smallest number of the given N numbers. They