2019牛客国庆集训派对day2C.Just h-index (二分答案+主席树区间数字个数查询)

mac2022-06-30  21

大致题意

给定一个长度为 n 的数列 ai,m 次询问。每次询问区间 [l,r] 的等级,一个区间的等级定义为 最大的i 满足区间内大于等于 i 的值的个数也大于等于 i 。 (1<=n<=1e5,1<=ai<=n)

思路

ai的值比较小,可以直接不用离散化主席树,每次查询区间 [l,r]的答案的时候,二分答案,在主席树上查询权值区间 [mid,n]的数的个数是否大于等于mid即可判断。复杂度nlognlogn。 牛客测评姬跳的厉害,同样的代码多交了几次居然就过了。不知道是不是假算法。

更新,赛后看运行比较快的代码,果然是nlogn的算法,就是主席树搜索答案的时候,直接顺便查询右侧的和是否大于mid ,如果满足则向右搜,然后就可以一直查询到答案,不需要二分答案。其实搜索向下的过程就是二分答案的过程和策略。%%%

代码

#include<bits/stdc++.h> using namespace std; #define maxn 100005 #define maxm 1006 #define ll long long int #define INF 0x3f3f3f3f #define inc(i,l,r) for(int i=l;i<=r;i++) #define dec(i,r,l) for(int i=r;i>=l;i--) #define mem(a) memset(a,0,sizeof(a)) #define sqr(x) (x*x) #define inf (ll)2e18+1 #define PI acos(-1) #define mod 10007 #define auto(i,x) for(int i=head[x];i;i=ed[i].nxt) ll read(){ ll x=0,f=1ll;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return f*x; } int n,m,a[maxn]; int cnt,root[maxn]; struct node{int l,r,sum;}T[maxn*20]; void update(int l,int r,int &x,int y,int pos){ T[++cnt]=T[y],T[cnt].sum++,x=cnt;///新开的树的的节点首先照搬上一次的结果 然后稍微更新一下 沿着这次的节点二分下去扩展出logn个节点的一条链 if(l==r)return ; int mid=(l+r)/2; if(mid>=pos)update(l,mid,T[x].l,T[y].l,pos); else update(mid+1,r,T[x].r,T[y].r,pos); T[x].sum=T[T[x].l].sum+T[T[x].r].sum; } int query(int l,int r,int x,int y,int lp,int rp){ if(lp<=l&&r<=rp)return T[y].sum-T[x].sum; int mid=(l+r)/2; int res=0; if(lp<=mid)res+=query(l,mid,T[x].l,T[y].l,lp,rp); if(mid+1<=rp) res+=query(mid+1,r,T[x].r,T[y].r,lp,rp); return res; } int main() { while(~scanf("%d%d",&n,&m)){ cnt=0; inc(i,1,n)a[i]=read(); inc(i,1,n)update(1,n,root[i],root[i-1],a[i]); int x,y; inc(i,1,m){ x=read();y=read(); int l=1,r=n,ans=1; while(l<=r){ int mid=(l+r)>>1; if(query(1,n,root[x-1],root[y],mid,n)>=mid){ ans=mid; l=mid+1; } else r=mid-1; } printf("%d\n",ans); } inc(i,0,cnt)T[i].l=T[i].r=T[i].sum=0; inc(i,0,n)root[i]=0; } return 0; }
最新回复(0)