链接:http://acm.hdu.edu.cn/showproblem.php?pid=6621
题意:T组样例(T<=3)。每组样例第一行给出n、q。接下来给出n个数。在接下来q个询问,每个询问给出l、r、p、k,但都要异或上一次的答案,才是真正的值。(也就是只能在线处理,不能离线处理。)每次询问,输出[l,r]区间内,第k小的|p-a[i]|。
思路:边更新边建立主席树,并且每次都不初始化。因为主席树本来就是每次插入值然后建起来的,即使是多个样例共用这个主席树也没事。对于每次询问,我们二分枚举a[i],看在[l,r]内,[p-a[i],p+a[i]]的个数是否大于等于k个,是得话就缩小a[i],否则就扩大a[i],这样一定能找到刚好只有k个值在该区间的a[i]。果然主席树不止差区间第k大,还可以查区间内在某个区间内的值的个数。详情请看注释。
PS:%%%%%%%%%坤神,tql。orzorzorzorzorzorzorzorzorzorzorzorzorzorzorzorzorzorzorzorzorz
#include
#include
#include
#include
#include
using namespace std;
#define M(a, b) memset(a, b, sizeof(a))
#define lowbit(x) (x&(-x))
typedef long long ll;
const int N=1e5+10;
const int M=1e6;
struct node
{
int l,r;
//l:其左子树的编号,即tree数组的下标
//r:其右子树的编号,即tree数组的下标
int val;
}tree[N * 55];
int n,q,a[N],root[N],cnt,limit,b[N],num;
int build(int l,int r)
{
int cur=cnt++;
tree[cur].val=0;
if(l==r)
{
tree[cur].l=0;
tree[cur].r=0;
return cur;
}
int mid=(r+l)>>1;
tree[cur].l=build(l,mid);
tree[cur].r=build(mid+1,r);
return cur;
}
int update(int up,int tar,int l,int r)
{
//给新节点编号
int cur=cnt++;
//主席树相当于前缀,所以记下上一个树节点的值
//相当于pre[i]=pre[i-1]
tree[cur]=tree[up];
//这一步相当于pre[i]=pre[i-1]+a[i]中的a[i]
//因为主席树记的是数的个数
tree[cur].val++;
//如果到了叶节点便返回
if(l==r) return cur;
int mid=(r+l)>>1;
//相当于线段树的点更新,只不过还要更新左或右子树的编号
if(tar<=mid) tree[cur].l=update(tree[up].l,tar,l,mid);
else tree[cur].r=update(tree[up].r,tar,mid+1,r);
return cur;
}
int query(int pl,int pr,int l,int r,int tl,int tr)
{
if(pl<=l&&r<=pr)
{
//前缀的思想
return tree[tr].val-tree[tl].val;
}
int m=(l+r)>>1,ans=0;
//查左右子树,传入左右子树的编号
if(pl<=m) ans+=query(pl,pr,l,m,tree[tl].l,tree[tr].l);
if(pr>=m+1) ans+=query(pl,pr,m+1,r,tree[tl].r,tree[tr].r);
return ans;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
root[i]=update(root[i-1],a[i],1,M);
}
//由于T很小,不必重新建树,空间换时间
//cnt=0;
//root[0]=build(1,M);
int l,r,k,p,ll,rr,m;
int ans=0;
while(q--)
{
scanf("%d%d%d%d",&l,&r,&p,&k);
l^=ans; r^=ans;
p^=ans; k^=ans;
if(l>r) swap(l,r);
//这里答案可能为0,所以ll初始化为0。
ll=0; rr=M;
while(ll<=rr)
{
m=(ll+rr)>>1;
//因为主席树l、r记的是编号,所以要多2个参数,记下编号
if(query(max(1,p-m),min(p+m,M),1,M,root[l-1],root[r])>=k)
{
ans=m;
rr=m-1;
}
else
ll=m+1;
}
printf("%d\n",ans);
}
}
return 0;
}