2019hdu暑假多校训练营第四场 H 主席树+二分

主席树+二分

二分与p的距离,确定与[p-mid,p+mid]这个区间中有多少个点,如果大于等于k个,说明这个区间合理,可以缩小寻找范围,否则增大寻找范围。

#include
using namespace std;
const int maxn=1e5+10;
int root[maxn*40];
int sum[maxn*40];
int ls[maxn*40];
int rs[maxn*40];
int a[maxn];
int tot,n,m;
void update(int &rt,int l,int r,int last,int p)
{
    rt=++tot;
    ls[rt]=ls[last];
    rs[rt]=rs[last];
    sum[rt]=sum[last]+1;
    if(l==r)
        return ;
    int mid=l+r>>1;
    if(p<=mid)
        update(ls[rt],l,mid,ls[last],p);
    else
        update(rs[rt],mid+1,r,rs[last],p);
}
int query(int l,int r,int x,int y,int k1,int k2)
{
    if(k1<=l&&r<=k2)
        return sum[y]-sum[x];
    int mid=l+r>>1;
    int ans=0;
    if(k1<=mid)
        ans+=query(l,mid,ls[x],ls[y],k1,k2);
    if(k2>mid)
    {
        ans+=query(mid+1,r,rs[x],rs[y],k1,k2);
    }
    return ans;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        tot=0;
        memset(root,0,sizeof(root));
        memset(ls,0,sizeof(ls));
        memset(rs,0,sizeof(rs));
        memset(sum,0,sizeof(sum));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            update(root[i],1,1000000,root[i-1],a[i]);
        }
        int ans=0;
        int x,y,p,k;
        while(m--)
        {
            scanf("%d%d%d%d",&x,&y,&p,&k);
            x^=ans;y^=ans;p^=ans;k^=ans;
            int l=0,r=1000000;
            while(l<=r)
            {
                int mid=l+r>>1;
                if(query(1,1000000,root[x-1],root[y],max(1,p-mid),min(1000000,p+mid))>=k)
                {
                    r=mid-1;
                    ans=mid;
                }
                else
                    l=mid+1;
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}

 

你可能感兴趣的:(主席树,二分答案)