主席树杂题积累 2016CCPC长春K SequenceII
题意:
给定 (n,m) ,一个长度为 (n) 的数组 (a) ,(m) 个询问。每个询问给出 (l,r) 要求输出第 (lceil{frac{k}{2}} ceil) 个在区间 ([l,r]) 第一次出现的元素的位置。 其中 (k) 是区间 ([l,r]) 中,不同元素的个数。
(1leq lleq rleq nleq 2 imes10^5) , (0leq a_i,mleq2 imes 10^5) 。
有 (T) 的测试样例,(Tleq2) 。
解:
建主席树,这里的主席树是指静态求第k小的那种主席树,即动态开点权值线段树。
从 (n) 到 (1) 建主席树。以下标为权值线段树的权值区间。
若对于当前的点 (i) ,(a_i) 在此之前没有出现过(即 (forall\,a_j[j>i],a_i e a_j) ),则对当前点的线段树,权值位置 (i) 的值 (+1) ,否则,记 (a_i) 在此前最后一次出现的位置为 (last_{a_i}),另建一个棵树 ((Tmp)) (这里的树均指主席树内里的树),这棵新树以 (T[i+1]) 为 (pre) ,并对其权值位置 (last_{a_i}) 的值 (-1) ,再对点 (i) 建一棵树( (T[i]) ),这颗树以 (Tmp) 为 (pre) ,权值位置 (i) 的值 (+1) 。
在这样的建立下,可以发现,对于 (T[i]),其权值区间,每一个有值的点都是在 (i) 之后其对应的 (a) 值第一次出现的点。
则 (T[l]) 的权值区间 ([l,r]) 的和对应数组 (a) 在区间 ([l,r]) 不同元素的个数。
再用求第k小的做法即可求对应第 (lceilfrac kn ceil) 个元素的位置。
叨叨:
在这个思路里,数组 (a) 的权值并没有起到什么作用,只是需要判断每个位置上的元素是否出现过,以及对于当前点记录之后元素第一次出现的位置 。
这种思路我没有想到;很好的一道题。
代码:
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int maxn=5e5+5;
const int mod=1e9+7;
int a[maxn],last[maxn];
int T[maxn],L[maxn<<4],R[maxn<<4],tnt,sum[maxn<<4];
void update(int&rt,int pre,int l,int r,int x,int v)
{
rt=++tnt;
sum[rt]=sum[pre]+v;L[rt]=L[pre],R[rt]=R[pre];
if(l==r)return;
int mid=(l+r)>>1;
if(x<=mid)update(L[rt],L[pre],l,mid,x,v);
else update(R[rt],R[pre],mid+1,r,x,v);
}
int query(int rt,int l,int r,int ql,int qr)
{
if(ql>r||qr<l)return 0;
if(ql<=l&&r<=qr)return sum[rt];
int mid=(l+r)>>1;
return query(L[rt],l,mid,ql,qr)+query(R[rt],mid+1,r,ql,qr);
}
int query1(int rt,int l,int r,int k)
{
if(l==r)return l;
int mid=(l+r)>>1;
if(sum[L[rt]]>=k)return query1(L[rt],l,mid,k);
else return query1(R[rt],mid+1,r,k-sum[L[rt]]);
}
int res[maxn];
int main()
{
int TT,cas=0;
scanf("%d",&TT);
while(TT--)
{
int n,m;
scanf("%d%d",&n,&m);
memset(last,0,sizeof last);
memset(T,0,sizeof T);tnt=0;
memset(L,0,sizeof L);
memset(R,0,sizeof R);
memset(sum,0,sizeof sum);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=n;i>=1;i--)
{
if(!last[a[i]])update(T[i],T[i+1],1,n,i,1);
else {
int tmp;
update(tmp,T[i+1],1,n,last[a[i]],-1);
update(T[i],tmp,1,n,i,1);
}
last[a[i]]=i;
}
int l,r,k,st,mid,p=0,tk;
int mm=0;
while(m--)
{
scanf("%d%d",&l,&r);
l=(l+p)%n+1;
r=(r+p)%n+1;
if(l>r)swap(l,r);
k=query(T[l],1,n,l,r);
tk=(k+1)>>1;
p=query1(T[l],1,n,tk);
// printf("%d %d
",k,p);
res[++mm]=p;
}
printf("Case #%d: ",++cas);
for(int i=1;i<=mm;i++)printf("%d%c",res[i],"
"[i==mm]);
}
}