题目地址: http://acm.hdu.edu.cn/showproblem.php?pid=4325
先离散化,然后建立线段树。
询问时,二分查找,转化成 离散化之后的新位置。
需要注意的是,离散化时区间间隔为2 。
原因很简单,1~3、7~10、1~10。若间隔为1,则离散化之后为: 1~2、3~4、1~4。
那么,4~6 这一段区间,只被包含于 1~10 的,但却不翼而飞了~~
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll (v<<1)
#define rr (v<<1|1)
#define tmid ((l+r)>>1)
using namespace std;
int p[maxn<<1|1];
int f[maxn<<1|1];
int rel[maxn<<1|1];
int n,nn,m,cnt;
bool tree[maxn<<2];
int num[maxn<<2];
int cmp(int x,int y){
return p[x]<p[y];
}
int deal(){
int i,k;
rel[f[0]]=k=1;
for(i=1;i<nn;i++){
if(p[f[i]]!=p[f[i-1]])
k+=2;
rel[f[i]]=k;
}
return k;
}
void tadd(int L,int R,int l,int r,int v){
if(L<=l && r<=R){
num[v]++;
return;
}
if(tree[v]==false){
tree[ll]=tree[rr]=false;
num[ll]=num[rr]=0;
tree[v]=true;
}
if(L<=tmid) tadd(L,R,l,tmid,ll);
if(R>tmid) tadd(L,R,tmid+1,r,rr);
}
int query(int x,int l,int r,int v){
if(tree[v]==false || l==r) return num[v];
if(x<=tmid) return num[v]+query(x,l,tmid,ll);
else return num[v]+query(x,tmid+1,r,rr);
}
int main(){
int t,tt,i,tmp,l,r,mid;
scanf("%d",&t);
for(tt=1;tt<=t;tt++){
scanf("%d%d",&n,&m);
nn=n<<1;
for(i=0;i<nn;i++) scanf("%d",&p[i]);
for(i=0;i<nn;i++) f[i]=i;
sort(f,f+nn,cmp);
cnt=deal();
/*
// 排序之后,即其对应的离散化数
for(i=0;i<nn;i++) cout<<p[f[i]]<<" ";cout<<endl;
for(i=0;i<nn;i++) cout<<rel[f[i]]<<" ";cout<<endl;
*/
tree[1]=false;
num[1]=0;
/*
// 离散化后的 原区间
for(i=0;i<nn;i+=2) cout<<"----"<<rel[f[i]]<<" "<<rel[f[i|1]]<<endl;
*/
for(i=0;i<nn;i+=2)
tadd(rel[i],rel[i|1],1,cnt,1);
printf("Case #%d:\n",tt);
while(m--){
scanf("%d",&tmp);
if(tmp<p[f[0]]) tmp=0;
else if(tmp==p[f[nn-1]]) tmp=rel[f[nn-1]];
else if(tmp>p[f[nn-1]]) tmp=rel[f[nn-1]]+1;
else{
l=0;r=nn-2;
while(l<=r){ // 二分查询,转化成离散化之后的数
mid=tmid;
if(p[f[mid]]==tmp){
tmp=rel[f[mid]];
break;
}
if(p[f[mid]]<tmp && tmp<p[f[mid+1]]){
tmp=rel[f[mid]]+1;
break;
}
if(p[f[mid]]>tmp)
r=mid-1;
else l=mid+1;
}
}
if(tmp<1 || tmp>cnt) puts("0");
else printf("%d\n",query(tmp,1,cnt,1));
}
}
return 0;
}