标签:
线段树(归并树)+二分查找
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 #include <string> 7 #include <vector> 8 #include <set> 9 #include <map> 10 #include <stack> 11 #include <queue> 12 #include <sstream> 13 #include <iomanip> 14 using namespace std; 15 typedef long long LL; 16 const int INF=0x4fffffff; 17 const int EXP=1e-5; 18 const int MS=100005; 19 20 struct node 21 { 22 int l,r; 23 vector<int> vec; 24 }nodes[4*MS]; 25 26 int a[MS]; 27 int num[MS]; 28 int n,m; 29 30 void build(int root,int l,int r) 31 { 32 nodes[root].l=l; 33 nodes[root].r=r; 34 nodes[root].vec.clear(); 35 if(r-l==1) 36 { 37 nodes[root].vec.push_back(a[l]); 38 return ; 39 } 40 int mid=(l+r-1)>>1; 41 build(root<<1,l,mid+1); 42 build(root<<1|1,mid+1,r); 43 nodes[root].vec.resize(r-l); 44 merge(nodes[root<<1].vec.begin(),nodes[root<<1].vec.end(), 45 nodes[root<<1|1].vec.begin(),nodes[root<<1|1].vec.end(),nodes[root].vec.begin()); 46 } 47 48 int query(int root,int l,int r,int x) 49 { 50 if(r<=nodes[root].l||nodes[root].r<=l) 51 return 0; 52 else if(nodes[root].l>=l&&nodes[root].r<=r) 53 return upper_bound(nodes[root].vec.begin(),nodes[root].vec.end(),x)-nodes[root].vec.begin(); 54 else 55 { 56 int lcnt=query(root<<1,l,r,x); 57 int rcnt=query(root<<1|1,l,r,x); 58 return lcnt+rcnt; 59 } 60 } 61 62 int main() 63 { 64 while(scanf("%d%d",&n,&m)!=EOF) 65 { 66 for(int i=0;i<n;i++) 67 { 68 scanf("%d",&a[i]); 69 num[i]=a[i]; 70 } 71 sort(num,num+n); 72 build(1,0,n); 73 int s,t,k; 74 for(int i=0;i<m;i++) 75 { 76 scanf("%d%d%d",&s,&t,&k); 77 s--; 78 int l=-1,r=n-1; 79 /* 注意 根据问题特点,这里应该是l=-1,r=n-1. 80 如果情况是询问[l,n]这个区间第n-l+1大的值,并且这个值在最后的位置,那么最后的结果会是num[n],越界 81 也就是说 二分的结果总是l=mid,知道r-l<=1; 细节问题需要注意 82 */ 83 while(r-l>1) 84 { 85 int mid=(l+r)>>1; 86 int cnt=query(1,s,t,num[mid]); 87 if(cnt>=k) 88 r=mid; 89 else 90 l=mid; 91 } 92 printf("%d\n",num[r]); 93 } 94 } 95 return 0; 96 }
我写的块状数组超时了。不知道是算法真的超时,还是细节问题导致超时。日后再来改正。
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <string> #include <vector> #include <set> #include <map> #include <stack> #include <queue> #include <sstream> #include <iomanip> using namespace std; typedef long long LL; const int INF=0x4fffffff; const int EXP=1e-5; const int MS=100005; const int SIZE=1000; int n,m; int a[MS]; int order[MS]; vector<int> bucket[MS/SIZE]; int main() { while(scanf("%d%d",&n,&m)!=EOF) { for(int i=0;i<MS/SIZE;i++) bucket[i].clear(); // 千万注意清空 for(int i=0;i<n;i++) { scanf("%d",&a[i]); bucket[i/SIZE].push_back(a[i]); order[i]=a[i]; } sort(order,order+n); for(int i=0;i<n/SIZE;i++) sort(bucket[i].begin(),bucket[i].end()); int s,t,k; while(m--) { scanf("%d%d%d",&s,&t,&k); s--; //[s,t) 二分查找使用左必有开更方便一些 int l=-1,r=n-1; // 注意: 根据问题的性质,l=0,r=n是错误的,因为有情况总是mid=l,一直到到 // n-l<=1, 这时答案是num[n],不在给定的数组范围内了。 while(r-l>1) { int mid=(l+r)>>1; int x=order[mid]; int tl=s,tr=t,c=0; // 处理区间两端多出的部分 while(tl<tr&&tl%SIZE!=0) if(a[tl++]<=x) c++; while(tl<tr&&tr%SIZE!=0) // 左闭右开 处理方便一些 if(a[--tr]<=x) c++; // 对每一个桶进行统计 while(tl<tr) { int id=tl/SIZE; c+=upper_bound(bucket[id].begin(),bucket[id].end(),x)-bucket[id].begin(); tl+=SIZE; } if(c>=k) r=mid; else l=mid; } printf("%d\n",order[r]); } } return 0; }
标签:
原文地址:http://www.cnblogs.com/767355675hutaishi/p/4391216.html