标签:
题目链接: 戳我
题目大意:Jiajia要为宠物狗,宠物狗按成一排站好(1 < i <= n),第 i 只狗的喜欢程度是 a[i], 之后他会先喂某个区间内第k个
即 n 个数, m个询问,接着是 n个数
接下来 m 行,每行是 l r k即 l 到 r 这个区间第 k 小的数,每个询问输出一个答案,即 a[i]
求区间第k大有很多算法, 详见此博客 【数据结构练习】 求区间第K大数的几种方法
我用的树状数组解法,来自
树状数组从前往后求和,用来解第k大(或小)的数 poj 2985 The k-th Largest Group
代码:
//Author LJH //www.cnblogs.com/tenlee #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <cmath> #include <algorithm> #include <vector> #include <queue> #include <stack> #include <map> #define clc(a, b) memset(a, b, sizeof(a)) using namespace std; const int inf = 0x3f; const int INF = 0x3f3f3f3f; const int maxn = 1e6+5; struct Que { int l, r, k, id; bool operator < (const Que &a) const { if(l == a.l) return r < a.r; return l < a.l; } }q[maxn]; struct Dog { int val, id; bool operator < (const Dog &a) const { return val < a.val; } }d[maxn]; int node[maxn], ans[maxn], fval[maxn], pval[maxn], n, m; inline int lowbit(int x) { return x & -x; } void Init() { clc(node, 0); } void add(int x, int val) { //printf("x = %d\n", x); if(x == 0) return; while(x <= n) { node[x] += val;//记录这个结点下面包含了多少个数,即这个结点总共有多少个叶子结点 x += lowbit(x); } } int findkth(int k)//查找第k大的数,返回第几个数,即排序后的下标 { int i, cnt = 0, ans = 0; for(i = 20; i >= 0; i--) //二进制思想 { ans += (1 << i); if(ans >= n || cnt + node[ans] >= k) ////这里大于等于k的原因是可能大小为ans的数不在c[ans]的控制范围之内,所以这里求的是 < k ans -= (1 << i); else cnt += node[ans]; //cnt用来累加比当前ans小的总组数 } //求出的ans是累加和(即小于等于ans的数的个数)小于k的情况下ans的最大值,所以ans+1就是第k大的数 return ans + 1; } int main() { int i, j; while(~scanf("%d %d", &n, &m)) { Init(); for(i = 1; i <= n; i++) { scanf("%d", &d[i].val); d[i].id = i; } sort(d+1, d+n+1); int k = 1; fval[d[1].id] = 1; pval[1] = d[1].val; for(i = 2; i <= n; i++) { if(d[i].val != d[i-1].val) pval[++k] = d[i].val; //消除重复的 fval[d[i].id] = k; //记录这个数是排序后第几个数, 即原来的第几个数对应现在排序后的第几个数 } for(i = 0; i < m; i++) { scanf("%d %d %d", &q[i].l, &q[i].r, &q[i].k); q[i].id = i; if(q[i].r < q[i].l) swap(q[i].l, q[i].r); } sort(q, q+m); int curl = 1, curr = 0; //左右 初始化左比右大 for(i = 0; i < m; i++) { if(curr < q[i].l)// curl curr q[i].l q[i].r { for(j = curl; j <= curr; j++)//fval[j] 获得 原来第j个数在排序后的坐标 add(fval[j], -1); //清空上次查询,即把树状数组不属于该区间的设为0, 下同 for(j = q[i].l; j <= q[i].r; j++) add(fval[j], 1); curl = q[i].l; curr = q[i].r; } else // curl q[i].l curr q[i].r || q[i].l curl curr q[i].r { for(j = curl; j < q[i].l; j++) // 清空上次查询,就是把不属于对应区间的 值改为0 add(fval[j], -1); for(j = curr+1; j <= q[i].r; j++) add(fval[j], 1); curl = q[i].l; curr = q[i].r; } ans[q[i].id] = pval[findkth(q[i].k)]; } for(int i = 0; i < m; i++) printf("%d\n", ans[i]); } return 0; }
划分树代码:
#include<cstdio> #include<iostream> #include<sstream> #include<cstdlib> #include<cstring> #include<string> #include<climits> #include<cmath> #include<algorithm> #include<queue> #include<vector> #include<stack> #include<set> #include<map> #define INF 0x3f3f3f3f #define eps 1e-8 using namespace std; const int MAXN=110000; int tr[MAXN<<2]; int sorted[MAXN],toleft[20][MAXN],val[20][MAXN]; void build(int l, int r, int dep, int rt) { if(l==r) { return; } int mid=(l+r)>>1; int lnum=mid-l+1; for(int i=l; i<=r; i++) { if(val[dep][i]<sorted[mid]) { lnum--; } } int lp=l,rp=mid+1; int cur_lnum=0; for(int i=l; i<=r; i++) { if(i==l) { toleft[dep][i]=0; } else { toleft[dep][i]=toleft[dep][i-1]; } if(val[dep][i]<sorted[mid]) { toleft[dep][i]++; val[dep+1][lp++]=val[dep][i]; } else if(val[dep][i]>sorted[mid]) { val[dep+1][rp++]=val[dep][i]; } else { if(cur_lnum<lnum) { cur_lnum++; toleft[dep][i]++; val[dep+1][lp++]=val[dep][i]; } else { val[dep+1][rp++]=val[dep][i]; } } } build(l,mid,dep+1,rt<<1); build(mid+1,r,dep+1,rt<<1|1); } int query(int l, int r, int L, int R, int k, int dep, int rt) { if(l==r) { return val[dep][l]; } int lnum,cur_lnum,rnum,cur_rnum; int mid=(l+r)>>1; if(l==L) { lnum=toleft[dep][R]; cur_lnum=0; } else { lnum=toleft[dep][R]-toleft[dep][L-1]; cur_lnum=toleft[dep][L-1]; } if(lnum>=k) { int newL=l+cur_lnum; int newR=l+lnum+cur_lnum-1; return query(l,mid,newL,newR,k,dep+1,rt<<1); } else { int rnum=R-L+1-lnum; int cur_rnum=L-l-cur_lnum; int newL=mid+cur_rnum+1; int newR=mid+cur_rnum+rnum; return query(mid+1,r,newL,newR,k-lnum,dep+1,rt<<1|1); } } int main() { int cas; int n, m; while(~scanf("%d %d", &n, &m)) { for(int i=0; i<n; i++) { scanf("%d",&val[0][i]); sorted[i]=val[0][i]; } sort(sorted,sorted+n); build(0,n-1,0,1); while(m--) { int l,r,k; scanf("%d%d%d",&l,&r,&k); l--; r--; printf("%d\n",query(0,n-1,l,r,k,0,1)); } } return 0; }
POJ 2761 Feed the dogs(树状数组求区间第K大)
标签:
原文地址:http://www.cnblogs.com/tenlee/p/4564505.html