题目链接:http://poj.org/problem?id=2104
题目意思很简单,就是给你一个序列,查询某区间第K大的数;
方法1:时间复杂度O(N*M);不支持更新操作,代码简单;
利用结构体排序,保留原数据的顺序。
#include <stdio.h> #include <iostream> #include <algorithm> #define N 100000 using namespace std; /* 这个思路很好;时间复杂度O(n*m); 不过还好这个题目给的数据量不大,而且时间限制给了2s,所以可以很轻松的过; */ struct NODE { int x,id; }node[N]; bool cmp(NODE a,NODE b) { return a.x<b.x; } int main() { int i,j,n,m,a,b,k; while(~scanf("%d%d",&n,&m)) { for(i=0;i<n;i++) { scanf("%d",&node[i].x); node[i].id=i; } sort(node,node+n,cmp); for(i=0;i<m;i++) { scanf("%d%d%d",&a,&b,&k); a--,b--; for(j=0;j<n;j++) { if(node[j].id>=a&&node[j].id<=b) k--; if(!k) { printf("%d\n",node[j].x); break; } } } } return 0; }
方法2:归并树,时间复杂度O(nlogn+mlong^3(n));利用归并排序,用线段树位数数组顺序;
#include<iostream> #include<string> #include<cstdio> #include<cstring> #include<queue> #include<map> #include<cmath> #include<stack> #include<set> #include<vector> #include<algorithm> #define LL long long #define inf 1<<30 #define s(a) scanf("%d",&a) #define Clear(a,b) memset(a,b,sizeof(a)) using namespace std; /* O(nlogn+mlog^3(n)) */ const int N=200005; int n,m,a,b; int seg[25][N]; int num[N]; void Merge_Sort(int l,int r,int deep) // 每一层保留该层已经排好的数据; { int mid=(l+r)>>1; int i=l,j=mid+1,k=l; while(i<=mid&&j<=r){ if(seg[deep+1][i]<=seg[deep+1][j]){ seg[deep][k++]=seg[deep+1][i++]; }else{ seg[deep][k++]=seg[deep+1][j++]; } } while(j<=r){ seg[deep][k++]=seg[deep+1][j++]; } while(i<=mid){ seg[deep][k++]=seg[deep+1][i++]; } } void Build(int l,int r,int rt,int deep) // 建树;deep根节点的深度; { if(l==r){ seg[deep][l]=num[l]; // 单叶子节点按输入的顺序存数据; return; } int mid=(l+r)>>1; Build(l,mid,rt<<1,deep+1); Build(mid+1,r,rt<<1|1,deep+1); Merge_Sort(l,r,deep); // 归并排序; } int Query(int v,int deep,int L,int R,int l,int r,int rt) // 查询[L,R]区间比v小的数有多少个; { if(r<L||R<l) return 0; if(L<=l&&R>=r){ return lower_bound(&seg[deep][l],&seg[deep][r]+1,v)-&seg[deep][l]; // 返回第一个大于等于v的下标; } int mid=(l+r)>>1; return Query(v,deep+1,L,R,l,mid,rt<<1)+Query(v,deep+1,L,R,mid+1,r,rt<<1|1); } int Solve(int l,int r,int k){ // 二分查找[L,R],降低时间复杂度; int L=1,R=n,mid; while(L<R){ mid=(L+R+1)>>1; // 这里要特别注意;mid=(L+R+1)>>1;不是mid=(L+R)>>1; int cnt=Query(seg[1][mid],1,l,r,1,n,1); // 返回比v小的个数; if(cnt<=k){ // 如果小于等于我们要查找的k,说明seg[1][mid]这个数太小了,网右边查找; L=mid; }else{ R=mid-1; } } return seg[1][L]; } int main() { while(~scanf("%d%d",&n,&m)){ for(int i=1;i<=n;i++) s(num[i]); Build(1,n,1,1); while(m--){ int l,r,k; s(l);s(r);s(k); printf("%d\n",Solve(l,r,k-1)); // 这里也值得注意一下,输入的是k-1,不是k; } } return 0; }
方法3:划分树;好吧,这个思路我还没有整理好,给这个牛逼算法留一块空地;^_^....