链接:
题意:
给一个序列, 有若干次询问(l,r),询问在该区间中的各个元素第一次出现的位置所组成的序列的中位数。
题解: 好吧一直没有懂主席树的逻辑,也是又一次遇到了这个题决定乘着假期学一学,至今也只是略懂。看大佬们的博客说这很显然是个主席树维护区间的题,因为是最早出现的位置(也就是最左边),所以从右向左读入,如果此时的a[i]值出现过,就把之前的影响删掉,然后再次普通修改。查询时只要查询root[l]就可以(因为对于(l,r)区间,root[l]覆盖了从右到l的区间,从而满足)。这里的关键是insert的写法,就是对于出现过的a[i],在普通修改时遵从的上一个应该是去掉影响的版本,具体见代码。
不得不提的是:大佬博客中的一句话我不是很理解点击打开链接(这里需要注意一下(其实是主席树基本用法需要注意的东西),当对某版本进行多次单点更新的时候,每次都需要新增一条路径,必须保证不对以往的版本造成影响。)不过反正一次更新新增路径多次更新增加路径也不会错吧,,以后懂了再来补。
吐槽一下:带修改的主席树和想象的很不一样(似乎叫树状数组+权值线段树更对),BZOJ1901看了很久别人的代码最后是个权限题。。
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+7;
struct node
{
int l,r,val;
}T[maxn*40];
int t,n,m;
int a[maxn],root[maxn],tot,last[maxn],ans[maxn];
void insert(int l,int r,int num,int x,int &y,int val)
{
int tmp=y;
y=++tot;
T[y]=tmp?T[tmp]:T[x];
T[y].val+=val;
int mid=(l+r)>>1;
if(l==r) return ;
if(num<=mid)
insert(l,mid,num,T[x].l,T[y].l,val);
else insert(mid+1,r,num,T[x].r,T[y].r,val);
}
int getsum(int l,int r,int k,int L,int R)
{
if(L<=l&&r<=R) return T[k].val;
int mid=(l+r)>>1;
int res=0;
if(L<=mid) res+=getsum(l,mid,T[k].l,L,R);
if(R>mid) res+=getsum(mid+1,r,T[k].r,L,R);
return res;
}
int query(int l,int r,int k,int num)
{
if(l==r) return l;
int mid=(l+r)>>1;
int tmp=T[T[k].l].val;
if(tmp>=num) return query(l,mid,T[k].l,num);
else return query(mid+1,r,T[k].r,num-tmp);
}
int main()
{
scanf("%d",&t);
int cas=0;
while(t--)
{
tot=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
memset(root,0,sizeof(root));
memset(last,0,sizeof(last));
memset(ans,0,sizeof(ans));
for(int i=n;i>=1;i--)
{
if(last[a[i]]) insert(1,n,last[a[i]],root[i+1],root[i],-1);
insert(1,n,i,root[i+1],root[i],1);
last[a[i]]=i;
}
for(int i=1;i<=m;i++)
{
int p,q;
scanf("%d%d",&p,&q);
int xx=(p+ans[i-1])%n+1;
int yy=(q+ans[i-1])%n+1;
int tmp=xx;
if(xx>yy)swap(xx,yy);
int sum=getsum(1,n,root[xx],xx,yy);
sum=(sum+1)>>1;
ans[i]=query(1,n,root[xx],sum);
}
printf("Case #%d: ",++cas);
for(int i=1;i<m;i++)
{
printf("%d ",ans[i]);
}
printf("%d\n",ans[m]);
}
}