题目链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1008&cid=851
题目大意:
给一个数组,每次给 l ,r, p, k,问区间 [l, r] 的数与 p 的绝对值的第 k 小,这个绝对值是多少。
思路:
把问题转化成求一个ans使得:区间[l, r]上值域 [p - ans, p + ans]的数的个数>=k。的ans的最小值。二分ans就可以了, 注意边界的处理。
#include <bits/stdc++.h>
#define LL long long
using namespace std;
#define mid (l+r)/2
const int maxn=2e5+10;
int root[maxn]={0}, L[maxn*60]={0}, R[maxn*60]={0}, sum[maxn*60]={0};
int n, m, cut=0, Len=0;
int BT(int l, int r)
{
int rt=++cut;
sum[rt]=0;
if(l<r)
{
L[rt]=BT(l, mid);
R[rt]=BT(mid+1, r);
}
return rt;
}
int gx(int i, int l, int r, int x)
{
int rt=++cut;
L[rt]=L[i], R[rt]=R[i], sum[rt]=sum[i]+1;
if(l<r)
{
if(x<=mid)
{
L[rt]=gx(L[i], l, mid, x);
}
else
{
R[rt]=gx(R[i], mid+1, r, x);
}
}
return rt;
}
int cx(int i, int l, int r, int x, int y)
{
if(x>r||x>y)
{
return 0;
}
if(l==x&&r==y)
{
return sum[i];
}
if(y<=mid)
{
return cx(L[i], l, mid, x, y);
}
else if(x>mid)
{
return cx(R[i], mid+1, r, x, y);
}
else
{
return cx(L[i], l, mid, x, mid)+cx(R[i], mid+1, r, mid+1, y);
}
}
int a[maxn], b[maxn];
int solve(int x)
{
int pl, pr, p, k;
scanf("%d%d%d%d", &pl, &pr, &p, &k);
pl^=x, pr^=x, p^=x, k^=x;
if(pl>pr)
{
swap(pl, pr);
}
int l=0, r=1e6+5, pmid=0;
while(l<=r)
{
int ans=(l+r)/2;
int t1=p-ans;
int t2=p+ans;
t1 = lower_bound(b+1, b+Len+1, t1) -b;
t2 = upper_bound(b+1, b+Len+1, t2) -b;
t2--;//右边界的处理
if(cx(root[pr], 1, Len, t1, t2)-cx(root[pl-1], 1, Len, t1, t2)>=k)
{
pmid=ans;
r=ans-1;
}
else
{
l=ans+1;
}
}
printf("%d\n", pmid);
return pmid;
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
cut=0;
memset(sum, 0, sizeof(sum));
scanf("%d%d", &n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d", &a[i]);
b[i]=a[i];
}
sort(b+1, b+n+1);
Len=unique(b+1, b+n+1)-(b+1);
root[0]=BT(1, Len);
for(int i=1;i<=n;i++)
{
a[i]=lower_bound(b+1, b+Len+1, a[i]) -b;
root[i]=gx(root[i-1], 1, Len, a[i]);
}
int x=0;
while(m--)
{
x=solve(x);
}
}
return 0;
}