标签:stream ace 离散化 题解 space 序列 二分查找 查找 turn
★★★ 输入文件:kth.in
输出文件:kth.out
简单对比
时间限制:1 s
内存限制:128 MB
题目描述
看到很短的题目会让人心情愉悦,所以给出一个长度为N的序列A1,A2,A3,...,AN,
现在有M个询问,每个询问都是Ai...Aj中第k小的数等于多少。
输入格式
第一行两个正整数N,M。
第二行N个数,表示序列A1,A2,...,AN。
紧着的M行,每行三个正整数i,j,k(k≤j-i+1),表示
询问Ai...Aj中第k小的数等于多少。
输出格式
共输出M行,第i行输出第i个询问的答案。
样例输入1:
4 3
4 1 2 3
1 3 1
2 4 3
1 4 4
样例输出1:
1
3
4
样例输入2:
5 5
4 2 9 9 10
1 3 1
2 4 3
1 4 4
3 5 2
2 5 2
样例输出2:
2
9
9
9
9
注释:
询问区间的第k小值并非严格第k小,例如样例2中第4个询问,询问3到5中第2小的数,
答案输出9,并不是严格第2小的10。
数据范围:
在50%的数据中,1<=N<=10000,1<=M<=10000,A[i]<=100000;
在100%的数据中,1<=N<=100000,1<=M<=100000,A[i]<=1000000;
[题解]
主席树经典应用,找区间第k小的数.
我做这题是在学treap之前学的,现在想想如果先学了treap应该写这题会非常简单吧...
我是想了两节课的做法....
其实这题的思想就是运用treap找第k小的数的思想;
回到正题: 主席树其实可以当做一个二维数据结构;(自己的理解)
以每个新建的线段树为一维,维护的信息为第二维
那么我们离散化,然后对数据进行排序
线段树维护排过序的数组中区间的数据个数(假如在1 号位那就是最小的数据)或者这样说,每个叶子节点对应的就是排序数组中的某个位置(位置可以二分查找快速找到);
然后対每个点在上一个线段树的基础上建立新的线段树(上一个线段树数据迁移过来了);
运用的思想是treap找k小数的思想,但是这题是区间k小,而treap的可视为1-n区间
若求[i-j]区间第k小数
那么就用第i-1棵线段树与第j棵进行操作;
这个区间第k小的数一定存在(默认)
查询操作同时在第i-1棵线段树和j棵线段树进行;(第i-1棵线段树维护的是1到i-1数据的信息,j同理)
那么到某一个区间时用第j棵的区间总数-第i-1棵的区间总数其实就是当前区间的数据总数;
那么就把区间的信息求出来了,此时转化为treap求k小数;
因为是排过序的
左儿子的数都比右儿子的小;
如果左儿子数据总数比k大那么k小数一定在左儿子,
否则,一定在右儿子,而且是第k-左儿子数据总数 小的数;
(我的写法不是由小到大排序的道理相同)
#include <iostream> #include <cstring> #include <cmath> #include <iomanip> #include <vector> #include <algorithm> #include <cstdio> #include <time.h> #include <map> #include <deque> #include <string> using namespace std; int n,t; int ppp[100010]; int data[100010]; int hash[100100]; int tot; int ls[5000010]; int rs[5000010]; int sum[5000010]; int gen[5000010]; int dian; inline bool cmp(int a,int b) { return a>b; } inline int erfen(int x) { int l=1;int r=tot; while(l+1<r) { int m=(l+r)/2; if(x<hash[m])l=m+1; else r=m; } if(hash[l]==x)return l; return r; } inline void gai(int shang,int &xin,int l,int r,int nl,int nr) { xin=++dian; if(l>=nl&&r<=nr) { sum[xin]=sum[shang]+1; return ; } ls[xin]=ls[shang]; rs[xin]=rs[shang]; int m=(l+r)>>1; if(m>=nl) { gai(ls[shang],ls[xin],l,m,nl,nr); } if(m<nr) { gai(rs[shang],rs[xin],m+1,r,nl,nr); } sum[xin]=sum[ls[xin]]+sum[rs[xin]]; } inline int find(int shang,int xin,int l,int r,int k) { if(l==r) { return l; } //int zuo=sum[ls[shang]]-sum[ls[xin]]; int you=sum[rs[xin]]-sum[rs[shang]]; int m=(l+r)>>1; if(you>=k) { return find(rs[shang],rs[xin],m+1,r,k); } else { return find(ls[shang],ls[xin],l,m,k-you); } } int main() { freopen("kth.in","r",stdin); freopen("kth.out","w",stdout); ios::sync_with_stdio(false); scanf("%d%d",&n,&t); for(int i=1;i<=n;i++) { scanf("%d",&ppp[i]); data[i]=ppp[i]; } sort(ppp+1,ppp+1+n,cmp); for(int i=1;i<=n;i++) { if(ppp[i]!=ppp[i-1]) { hash[++tot]=ppp[i]; } } for(int i=1;i<=n;i++) { int u=erfen(data[i]); gai(gen[i-1],gen[i],1,tot,u,u); } while(t--) { int x,y,z; scanf("%d%d%d",&x,&y,&z); printf("%d\n",hash[find(gen[x-1],gen[y],1,tot,z)]); //cout<<x<<y<<z<<" "<<find(gen[x-1],gen[y],1,tot,z)<<endl; } return 0; }
欢迎指错,或者改进,我的表达可能不太好.
标签:stream ace 离散化 题解 space 序列 二分查找 查找 turn
原文地址:http://www.cnblogs.com/zffisher/p/7143615.html