【线段树】【积累】主席树杂题积累 2016CCPC长春K SequenceII
主席树杂题积累 2016CCPC长春K SequenceII
题意:
给定 \(n,m\) ,一个长度为 \(n\) 的数组 \(a\) ,\(m\) 个询问。每个询问给出 \(l,r\) 要求输出第 \(\lceil{\frac{k}{2}}\rceil\) 个在区间 \([l,r]\) 第一次出现的元素的位置。 其中 \(k\) 是区间 \([l,r]\) 中,不同元素的个数。
\(1\leq l\leq r\leq n\leq 2\times10^5\) , \(0\leq a_i,m\leq2\times 10^5\) 。
有 \(T\) 的测试样例,\(T\leq2\) 。
解:
建主席树,这里的主席树是指静态求第k小的那种主席树,即动态开点权值线段树。
从 \(n\) 到 \(1\) 建主席树。以下标为权值线段树的权值区间。
若对于当前的点 \(i\) ,\(a_i\) 在此之前没有出现过(即 \(\forall\,a_j[j>i],a_i\ne 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小的做法即可求对应第 \(\lceil\frac kn\rceil\) 个元素的位置。
叨叨:
在这个思路里,数组 \(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\n",k,p);
res[++mm]=p;
}
printf("Case #%d: ",++cas);
for(int i=1;i<=mm;i++)printf("%d%c",res[i]," \n"[i==mm]);
}
}