HDU 6621 K-th Closest Distance 划分树+暴力

http://acm.hdu.edu.cn/showproblem.php?pid=6621
You have an array: a1, a2, , an and you must answer for some queries.
For each query, you are given an interval [L, R] and two numbers p and K. Your goal is to find the Kth closest distance between p and aL, aL+1, …, aR.
The distance between p and ai is equal to |p - ai|.
For example:
A = {31, 2, 5, 45, 4 } and L = 2, R = 5, p = 3, K = 2.
|p - a2| = 1, |p - a3| = 2, |p - a4| = 42, |p - a5| = 1.
Sorted distance is {1, 1, 2, 42}. Thus, the 2nd closest distance is 1.
Input
The first line of the input contains an integer T (1 <= T <= 3) denoting the number of test cases.
For each test case:
冘The first line contains two integers n and m (1 <= n, m <= 10^5) denoting the size of array and number of queries.
The second line contains n space-separated integers a1, a2, …, an (1 <= ai <= 10^6). Each value of array is unique.
Each of the next m lines contains four integers L’, R’, p’ and K’.
From these 4 numbers, you must get a real query L, R, p, K like this:
L = L’ xor X, R = R’ xor X, p = p’ xor X, K = K’ xor X, where X is just previous answer and at the beginning, X = 0.
(1 <= L < R <= n, 1 <= p <= 10^6, 1 <= K <= 169, R - L + 1 >= K).
Output
For each query print a single line containing the Kth closest distance between p and aL, aL+1, …, aR.
Sample Input

1
5 2
31 2 5 45 4
1 5 5 1
2 5 3 2

Sample Output

0
1

题目大意:给一个序列 a a a,每次询问给出四个数 L 、 R 、 P 、 K L、R、P、K LRPK,设上一次询问的的答案为 a n s ans ans,做操作: L ⊕ a n s , R ⊕ a n s , P ⊕ a n s , K ⊕ a n s L\oplus ans,R\oplus ans,P\oplus ans,K\oplus ans Lans,Rans,Pans,Kans后,计算新序列: ∣ P − a L ∣ , ∣ P − a L + 1 ∣ , … … , ∣ P − a R ∣ |P-a_{L}|,|P-a_{L+1}|,……,|P-a_{R}| PaL,PaL+1,,PaR中第 K K K小的数。
思路:正解应该是主席树+二分。但是当时满脑子只有暴力,而划分树求第 k k k小又比主席树快了很多,于是就有了划分树+暴力的做法。考虑找到一个位置 p o s pos pos满足: a p o s &lt; = P a_{pos}&lt;=P apos<=P a p o s + 1 &gt; P a_{pos+1}&gt;P apos+1>P,然后就从 p o s pos pos这个位置开始向两端暴力枚举即可。这个 p o s pos pos可以通过二分得到。

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100005

#define MID ((l+r)>>1)
int a[N],s[N],tree[20][N],num[20][N],n,m;

void Build(int c,int l,int r)
{
    int lm=MID-l+1,lp=l,rp=MID+1;
    for(int i=l;i<=MID;i++) //左侧与中位数相等的数的个数
        lm-=s[i]<s[MID];
    for(int i=l;i<=r;i++)
    {
        if(i==l)
            num[c][i]=0;
        else
            num[c][i]=num[c][i-1];
        if(tree[c][i]==s[MID])//中位数
        {
            if(lm)//左侧还要放
            {
                lm--;
                num[c][i]++;
                tree[c+1][lp++]=tree[c][i];
            }
            else
                tree[c+1][rp++]=tree[c][i];
        }
        else if(tree[c][i]<s[MID])
        {
            num[c][i]++;
            tree[c+1][lp++]=tree[c][i];
        }
        else
            tree[c+1][rp++]=tree[c][i];
    }
    if(l<r)
        Build(c+1,l,MID),Build(c+1,MID+1,r);
}

int query(int c,int l,int r,int ql,int qr,int k)
{
    if(l==r)
        return tree[c][l];
    int s,ss;
    if(l==ql)
        s=0,ss=num[c][qr];
    else
        s=num[c][ql-1],ss=num[c][qr]-num[c][ql-1];
    if(k<=ss)
        return query(c+1,l,MID,l+s,l+s+ss-1,k);
    else
        return query(c+1,MID+1,r,MID+1+ql-l-s,MID+1+qr-l-s-ss,k-ss);
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            s[i]=tree[0][i]=a[i];
        }
        sort(s+1,s+1+n);
        Build(0,1,n);
        int L,R,md,tmp,l,r,p,k,cnt,ans=0;
        for(int i=1;i<=m;i++)
        {
            scanf("%d %d %d %d",&l,&r,&p,&k);
            l^=ans,r^=ans,p^=ans,k^=ans;
            L=1,R=r-l+1;
            while(L<=R)
            {
                md=(L+R)>>1;
                tmp=query(0,1,n,l,r,md);
                if(tmp<=p)
                    L=md+1;
                else
                    R=md-1;
            }
            if(R<=0)//p al …… ar
                ans=query(0,1,n,l,r,k)-p;
            else if(R>=r-l+1)//al …… ar p
                ans=p-query(0,1,n,l,r,r-l-k+2);
            else
            {
                cnt=0;
                int i=R,j=R+1;
                int tmp1,tmp2;
                tmp1=query(0,1,n,l,r,i);
                tmp2=query(0,1,n,l,r,j);
                while(cnt<k)
                {
                    if(p-tmp1<=tmp2-p)
                    {
                        ++cnt;
                        ans=p-tmp1;
                        --i;
                        if(i>=1)
                            tmp1=query(0,1,n,l,r,i);
                        else
                            tmp1=-INF;
                    }
                    else
                    {
                        ++cnt;
                        ans=tmp2-p;
                        ++j;
                        if(j<=r-l+1)
                            tmp2=query(0,1,n,l,r,j);
                        else
                            tmp2=INF;
                    }
                }
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值