POJ 2104&&51nod 1175&&Joyoi 动态排名

静态区间第K大

题目链接:http://poj.org/problem?id=2104

K-th Number
Time Limit: 20000MS Memory Limit: 65536K
Total Submissions: 64800 Accepted: 22822
Case Time Limit: 2000MS

Description

You are working for Macrohard company in data structures department. After failing your previous task about key insertion you were asked to write a new data structure that would be able to return quickly k-th order statistics in the array segment. 
That is, given an array a[1...n] of different integer numbers, your program must answer a series of questions Q(i, j, k) in the form: "What would be the k-th number in a[i...j] segment, if this segment was sorted?" 
For example, consider the array a = (1, 5, 2, 6, 3, 7, 4). Let the question be Q(2, 5, 3). The segment a[2...5] is (5, 2, 6, 3). If we sort this segment, we get (2, 3, 5, 6), the third number is 5, and therefore the answer to the question is 5.

Input

The first line of the input file contains n --- the size of the array, and m --- the number of questions to answer (1 <= n <= 100 000, 1 <= m <= 5 000). 
The second line contains n different integer numbers not exceeding 10 9 by their absolute values --- the array for which the answers should be given. 
The following m lines contain question descriptions, each description consists of three numbers: i, j, and k (1 <= i <= j <= n, 1 <= k <= j - i + 1) and represents the question Q(i, j, k).

Output

For each question output the answer to it --- the k-th number in sorted a[i...j] segment.

Sample Input

7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3

Sample Output

5
6
3

Hint

This problem has huge input,so please use c-style input(scanf,printf),or you may got time limit exceed.


code:

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<cstring>

using namespace std;

const int maxn = 2e6+5;
const int mxb = 1e5+5;

struct node{
	int l,r,sum;
	int ch[2];
}tr[maxn];

int tot;
int sz;
int a[mxb];
int b[mxb];
int rt[mxb];

void build(int &x,int L,int R){
	x = ++ tot;
	tr[x].l=L;
	tr[x].r=R;
	if(L==R) return ;
	int M=(L+R)>>1;
	build(tr[x].ch[0],L,M);
	build(tr[x].ch[1],M+1,R);
}

void insert(int pre,int &x,int pos){
	x = ++ tot;
	tr[x]=tr[pre];
	tr[x].sum++;
	if(tr[x].l==tr[x].r) return ;
	int m=(tr[x].l+tr[x].r)>>1;
	if(pos<=m) insert(tr[pre].ch[0],tr[x].ch[0],pos);
	else insert(tr[pre].ch[1],tr[x].ch[1],pos);
}

int query(int pre,int x,int k){
	if(tr[x].l==tr[x].r) {
		return a[tr[x].l];
	}
	int rs=tr[tr[x].ch[0]].sum-tr[tr[pre].ch[0]].sum;
	if(rs>=k) return query(tr[pre].ch[0],tr[x].ch[0],k);
	else return query(tr[pre].ch[1],tr[x].ch[1],k-rs);
}

int main(){
	int n,q;
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		b[i]=a[i];
	}
	sort(a+1,a+1+n);
	sz = unique(a+1,a+n+1)-(a+1);
	build(rt[0],1,sz);
	for(int i=1;i<=n;i++){
		int pos=lower_bound(a+1,a+sz+1,b[i])-a;
		insert(rt[i-1],rt[i],pos);
	}
	while(q--){
		int l,r,k;
		scanf("%d%d%d",&l,&r,&k);
		int ans=query(rt[l-1],rt[r],k);
		printf("%d\n",ans);
	}
	return 0;
}


动态区间第K大

题目链接:http://www.joyoi.cn/problem/tyvj-3070

题目大意:


题目限制

时间限制内存限制评测方式题目来源
1000ms65536KiB标准比较器Local

题目描述

给定一个长度为N的已知序列Ai,要求维护这个序列,能够支持以下两种操作: 
1、查询A[i],A[i+1],A[i+2],...,Aj中,升序排列后排名第k的数。 
2、修改A[i]的值为j。 

所谓排名第k,指一些数按照升序排列后,第k位的数。例如序列{6,1,9,6,6},排名第3的数是6,排名第5的数是9。

输入格式

第一行包含一个整数D(0<=D<=4),表示测试数据的数目。接下来有D组测试数据,每组测试数据中,首先是两个整数N( 1 <= N <= 50000 ),M( 1 <= M <= 10000 ),表示序列的长度为N,有M个操作。接下来的N个不大于1,000,000,000正整数,第i个表示序列A[i]的初始值。然后的M行,每行为一个操作 
Q i j k 或者 
C i j 
分别表示查询A[i],A[i+1],A[i+2],...,Aj中,升序排列后排名第k的数,和修改A[i]的值为j。

输出格式

对于每个查询,输出一行整数,为查询的结果。测试数据之间不应有空行。






















sol:

用树状数组处理前缀和。

有个优化,先建好原树,然后新建一个修改树,所有的更新都在修改树上进行,计算的时候加上修改树的值。


code:

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<cstring>

using namespace std;

const int maxn = 6e4 + 5;

struct node {
	int ch[2];
	int sum;
};

node tr[maxn << 5];

int tot, top, sz, sl, sr;
int a[maxn], b[maxn];
int L[maxn], R[maxn];
int rt[2][maxn];
int op[maxn][3];
int n, q;
bool flag[maxn];

int inline lowbit(int x) { return x&(-x); }

void update( int pre, int l, int r, int &x, int pos, int o) {
	x = ++ tot;
	tr[x] = tr[pre];
	tr[x].sum += o;
	if (l == r) return;
	int m = (l + r) >> 1;
	if (pos <= m) update(tr[pre].ch[0], l, m, tr[x].ch[0], pos, o);
	else update(tr[pre].ch[1], m + 1, r, tr[x].ch[1], pos, o);
}

void build(int &x, int l, int r) {
	x = ++tot;
	tr[x].sum = 0;
	if (l == r) return;
	int mid = (l + r) >> 1;
	build(tr[x].ch[0], l, mid);
	build(tr[x].ch[1], mid + 1, r);
}

int query(int l, int r, int k) {
	l--;
	for (int j = l; j > 0; j -= lowbit(j)) 	L[++sl] = rt[1][j];
	for (int j = r; j > 0; j -= lowbit(j)) 	R[++sr] = rt[1][j];
	int lx = 1, rx = sz;
	int Tl = rt[0][l];
	int Tr = rt[0][r];
	while (lx < rx) {
		int mid = (lx + rx) >> 1 ;
		int tmp = tr[tr[Tr].ch[0]].sum - tr[tr[Tl].ch[0]].sum;
		for (int i = 1; i <= sl; i++) tmp -= tr[tr[L[i]].ch[0]].sum;
		for (int i = 1; i <= sr; i++) tmp += tr[tr[R[i]].ch[0]].sum;
		if (k <= tmp) {
			rx = mid;
			for (int i = 1; i <= sl; i++) L[i] = tr[L[i]].ch[0];
			for (int i = 1; i <= sr; i++) R[i] = tr[R[i]].ch[0];
			Tl = tr[Tl].ch[0];
			Tr = tr[Tr].ch[0];
		}
		else {
			lx = mid + 1;
			k -= tmp;
			for (int i = 1; i <= sl; i++) L[i] = tr[L[i]].ch[1];
			for (int i = 1; i <= sr; i++) R[i] = tr[R[i]].ch[1];
			Tl = tr[Tl].ch[1];
			Tr = tr[Tr].ch[1];
		}
	}
	return lx;
}

void init() {
	tot = sz = sl = sr = top = 0;
}

int inline Pos(int x) {
	return lower_bound(b + 1, b + sz + 1, x) - b;
}

int main() {
	int t;
	scanf("%d", &t);
	while (t--) {
		init();
		scanf("%d%d", &n, &q);
		for (int i = 1; i <= n; i++) {
			scanf("%d", &a[i]);
			b[++top] = a[i];
		}
		char s[3];
		for (int i = 1; i <= q; i++) {
			scanf("%s", s);
			scanf("%d%d", &op[i][0], &op[i][1]);
			if (s[0] == 'Q') {
				scanf("%d", &op[i][2]);
				flag[i] = true;
			}
			else {
				b[++top] = op[i][1];
				flag[i] = false;
			}
		}
		sort(b + 1, b + top + 1);
		sz = unique(b + 1, b + top + 1) - (b + 1);
		build(rt[0][0], 1, sz);
		for (int i = 1; i <= n; i++) rt[1][i] = rt[0][0];
		for (int i = 1; i <= n; i++) {
			update(rt[0][i - 1], 1, sz, rt[0][i], Pos(a[i]), 1);
		}
		for (int i = 1; i <= q; i++) {
			if (flag[i]) {
				sl = sr = 0;
				int ans = query(op[i][0], op[i][1], op[i][2]);
				printf("%d\n", b[ans]);
			}
			else{
				int p_i = op[i][0];
				for (int j = p_i; j <= n; j += lowbit(j)) {
					int p_r = rt[1][j];
					update(p_r, 1, sz, rt[1][j], Pos(a[p_i]), -1);
				}
				a[p_i] = op[i][1];
				for (int j = p_i; j <= n; j += lowbit(j)) {
					int p_r = rt[1][j];
					update(p_r, 1, sz, rt[1][j], Pos(a[p_i]), 1);
				}
			}
		}
	}
	return 0;
}


        最后小结一下:主席树为序列所有的前缀维护一颗线段树 。各个线段树维护的是对应权值区间的数出现的个数(根结点维护1到最大权值的数出现次数),如果数据范围较大则需要离散化,这样就需要离线处理。于是对于区间(l,r)第k大的查询,求出前l-1区间的结果suml和前r区间的结果sumr,两者相减就是(l,r)区间在对应权值范围的个数,递归处理即可。注意到每次修改值只会影响上颗线段树上从修改权值叶子到根的一条链,其余结点都可以重用。同理,可持久化线段树就是复用上个版本的结点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值