题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4417
题意:
给定一个序列,
每次询问[l,r]区间内<=x的元素的个数
算法:
本题解法非常多。
以后我会慢慢补全这篇文章,
当做一次基础数据结构训练。
划分树 (218ms)
#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 x, int dep, int rt) {
if(l==r) {
return val[dep][l]<=x;
}
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(sorted[mid]>x) {
if(!lnum) {
return 0;
}
int newL=l+cur_lnum;
int newR=l+lnum+cur_lnum-1;
return query(l,mid,newL,newR,x,dep+1,rt<<1);
} else {
int rnum=R-L+1-lnum;
int cur_rnum=L-l-cur_lnum;
if(!rnum) {
return lnum;
}
int newL=mid+cur_rnum+1;
int newR=mid+cur_rnum+rnum;
return lnum+query(mid+1,r,newL,newR,x,dep+1,rt<<1|1);
}
}
int main() {
int cas;
scanf("%d",&cas);
for(int T=1; T<=cas; T++) {
int n,m;
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);
printf("Case %d:\n",T);
while(m--) {
int l,r,x;
scanf("%d%d%d",&l,&r,&x);
printf("%d\n",query(0,n-1,l,r,x,0,1));
}
}
return 0;
}
划分树+二分 (562ms)
#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;
scanf("%d",&cas);
for(int T=1; T<=cas; T++) {
int n,m;
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);
printf("Case %d:\n",T);
while(m--) {
int x,llim,rlim;
scanf("%d%d%d",&llim,&rlim,&x);
int l=0,r=rlim-llim+1;
while(l<r) {
int mid=(l+r+1)>>1;
if(query(0,n-1,llim,rlim,mid,0,1)<=x) {
l=mid;
} else {
r=mid-1;
}
}
printf("%d\n",l);
}
}
return 0;
}
主席树(125ms)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<sstream>
#include<cstdlib>
#include<cstring>
#include<string>
#include<climits>
#include<cmath>
#include<queue>
#include<vector>
#include<stack>
#include<set>
#include<map>
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
const int MAXN = 110000;
const int MAXM = 2100000;
int tot;
typedef struct
{
int cot, right, left;
} NODE;
int root[MAXN],a[MAXN];
vector<int> hash;
NODE node[MAXM];
int newnode(int cot, int left, int right)
{
node[tot].cot = cot;
node[tot].left = left;
node[tot].right = right;
return tot ++;
}
int insert(int rt, int l, int r, int k)
{
if(l <= k && k <=r)
{
if(l == r)
{
return newnode(node[rt].cot + 1, 0, 0);
}
int mid = (l + r) >> 1;
return newnode(node[rt].cot + 1,
insert(node[rt].left, l, mid, k),
insert(node[rt].right, mid + 1, r, k));
}
return rt;
}
int query(int p, int q, int l, int r, int k)
{
if(hash[r] <= k)
{
return node[q].cot - node[p].cot;
}
if(l == r)
{
return 0;
}
int mid = (l + r) >> 1;
int ret = query(node[p].left, node[q].left, l, mid, k);
if(k >= hash[mid+1])
{
ret += query(node[p].right, node[q].right, mid + 1, r, k);
}
return ret;
}
int main()
{
int cas;
scanf("%d", &cas);
for(int T = 1; T <= cas; T ++)
{
int n, q;
scanf("%d %d", &n, &q);
tot=0;
root[0]=newnode(0, 0, 0);
hash.clear();
for(int i = 1; i <= n; i ++)
{
scanf("%d", &a[i]);
hash.push_back(a[i]);
}
sort(hash.begin(), hash.end());
hash.erase(unique(hash.begin(), hash.end()), hash.end());
int m = hash.size();
for(int i = 1; i <= n; i ++)
{
a[i] = lower_bound(hash.begin(), hash.end(), a[i]) - hash.begin();
root[i] = insert(root[i - 1], 0, m - 1, a[i]);
}
printf("Case %d:\n",T);
while(q --)
{
int l, r, k;
scanf("%d %d %d",&l, &r, &k);
printf("%d\n", query(root[l], root[r + 1], 0, m - 1, k));
}
}
return 0;
}
本文介绍了解决区间查询问题的三种高效算法:划分树、划分树+二分以及主席树,并提供了详细的代码实现。
465

被折叠的 条评论
为什么被折叠?



