Super Mario
题目要求给出一个区间,求出区间【l,r】之内小于等于给定H的数的个数. 我们可以类比求区间第k大的方法去求区间小于等于H的,
有一个技巧就是先将H读入,将H和原数据一起离散化,但是建树的时候依然只建n棵树,最后每次求一下[0-r] [0-l-1]
做差就可以了(注意题目区间是【0,n-1】)。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define N 100000
int a[N*4],b[N*4];
int sum[N*20],T[N*20];
int L[N*20],R[N*20];
int cnt=0;
void build(int &rt,int l,int r)
{
rt=++cnt;
sum[rt]=0;
if(l==r)return ;
int m=(l+r)>>1;
build(L[rt],l,m);
build(R[rt],m+1,r);
}
void update(int &rt,int pre,int l,int r,int x)
{
rt=++cnt;
L[rt]=L[pre],R[rt]=R[pre];
sum[rt]=sum[pre]+1;
if(l==r)return ;
int m=(l+r)>>1;
if(x<=m)update(L[rt],L[pre],l,m,x);
else update(R[rt],R[pre],m+1,r,x);
}
int query(int rt,int l,int r,int k)
{
int ans=0;
while(l<r)
{
int mid=(l+r)>>1;
if(k<mid)
{
rt=L[rt];
r=mid;
}
else
{
ans+=sum[L[rt]];
rt=R[rt];
l=mid+1;
}
}
return ans;
}
int be[N],en[N],H[N];
int main()
{
int t;
scanf("%d",&t);
for(int cas=1;cas<=t;cas++)
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
}
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&be[i],&en[i],&H[i]);
b[i+n]=H[i];
}
sort(b+1,b+1+n+m);
int len=unique(b+1,b+1+n+m)-b-1;
cnt=0;
build(T[0],1,len);
for(int i=1;i<=n;i++)
{
int pos=lower_bound(b+1,b+1+len,a[i])-b;
update(T[i],T[i-1],1,len,pos);
}
printf("Case %d:\n",cas);
for(int i=1;i<=m;i++)
{
int p1=lower_bound(b+1,b+1+len,H[i])-b;
int k1=query(T[en[i]+1],1,len,p1);
int k2=query(T[be[i]],1,len,p1);
printf("%d\n",k1-k2);
}
}
return 0;
}
参考了大牛的题解发现可以用树状数组离线处理,具体思路是这样的。
对于区间里的每一个值,我们按照从小到大排序,然后我们按照位置依次插入
同样的,对于要查询的H我们也将它从小到大排序,这样我们就能保证,当查询到较大的H时
比H小的值都已经插进树状数组里了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100005
#define lowb(x) x&-x;
struct node
{
int k,id;
}a[N];
struct node1
{
int l,r,h,id;
}H[N];
int L[N],R[N];
int C[N];
int ans[N];
bool cmp(node x,node y)
{
return x.k<y.k;
}
bool cmp1(node1 x,node1 y)
{
return x.h<y.h;
}
void add(int pos)
{
while(pos<=N)
{
C[pos]+=1;
pos+=lowb(pos);
}
}
int getsum(int x)
{
int ans=0;
while(x)
{
ans+=C[x];
x-=lowb(x);
}
return ans;
}
void solve(int m,int n)
{
int top=1;
for(int i=1;i<=m;i++)
{
while(top<=n&&a[top].k<=H[i].h)
{
add(a[top].id);
top++;
}
// printf("%d %d %d\n",H[i].l,H[i].r,H[i].h);
// printf("%d %d\n",getsum(H[i].l-1),getsum(H[i].r));
ans[H[i].id]=getsum(H[i].r)-getsum(H[i].l-1);
}
for(int i=1;i<=m;i++)
{
printf("%d\n",ans[i]);
}
}
int main()
{
int t;
cin>>t;
for(int cas=1;cas<=t;cas++)
{
int n,m;
memset(C,0,sizeof(C));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i].k);
a[i].id=i;
}
sort(a+1,a+1+n,cmp);
int x,y;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&H[i].h);
H[i].l=x+1,H[i].r=y+1;
H[i].id=i;
}
sort(H+1,H+1+m,cmp1);
printf("Case %d:\n",cas);
solve(m,n);
}
}
补充:
学习了划分树之后,本题又有了另一种快速简单的解法。就是二分枚举区间第k大的值。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define N 100005
int t[20][N],tl[20][N];
int sor[N];
void build(int l,int r,int dep)
{
if(l==r)return ;
int m=(l+r)>>1;
int same = m-l+1;
for(int i=l;i<=r;i++)if(t[dep][i]<sor[m])same--;
int ln=l;
int rn=m+1;
for(int i=l;i<=r;i++)
{
if(t[dep][i]<sor[m]||(t[dep][i]==sor[m]&&same>0))
{
t[dep+1][ln++]=t[dep][i];
if(t[dep][i]==sor[m])same--;
tl[dep][i]=tl[dep][l-1]+ln-l;
}
else
{
t[dep+1][rn++]=t[dep][i];
tl[dep][i]=tl[dep][i-1];
}
}
build(l,m,dep+1);
build(m+1,r,dep+1);
}
int query(int l,int r,int ql,int qr,int k,int dep)
{
if(l==r)return t[dep][l];
int m=(l+r)>>1;
int cnt=tl[dep][qr]-tl[dep][ql-1];
if(cnt>=k)
{
int newl=l+tl[dep][ql-1]-tl[dep][l-1];
int newr=newl+cnt-1;
return query(l,m,newl,newr,k,dep+1);
}
else
{
int newr=qr+tl[dep][r]-tl[dep][qr];
int newl=newr-(qr-ql-cnt);
return query(m+1,r,newl,newr,k-cnt,dep+1);
}
}
void solve(int n,int ql,int qr,int h)
{
int l=1;
int r=(qr-ql)+1;
while(l<=r)
{
int m=(l+r)>>1;
int tmp=query(1,n,ql,qr,m,0);
if(tmp<=h)
{
l=m+1;
}else r=m-1;
}
printf("%d\n",r);
}
int main()
{
int tt;
cin>>tt;
int cas=1;
while(tt--)
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&t[0][i]);
sor[i]=t[0][i];
}
sort(sor+1,sor+1+n);
build(1,n,0);
printf("Case %d:\n",cas++);
while(m--)
{
int l,r,h;
scanf("%d%d%d",&l,&r,&h);
solve(n,l+1,r+1,h);
}
}
}