题目
给你一个长度为n的排列,每次询问由l,r构成,你需要回答区间[l,r]形成的连续段数,连续段由是由多个相邻的数构成。
例如1,2,3,4为一个连续段,1,3是两个连续段.
对于一个长度为5的排列为:1,3,5,2,4。询问区间为[2,4]时,连续段数量为2,即2-3与5。
本题强制再线,真实的询问区间需要异或上一次答案,
l=l⨁lastans,r=r⨁lastans,其中⨁表示异或运算,最开始lastans=0。
询问区间不合法,即l<1或者l>r或者r>n时,忽略本次查询
思路来源
my学长
题解
考虑一个区间[l,r]里的数,初始认为每个都是独立的一段,即r-l+1段,
单独考虑ai和ai+1,对于段数减少带来的影响,
考虑合并,对于ai来说,
如果ai+1也位于[l,r]内,则ai和ai+1是一段,可合并,导致最终少一段;
如果ai+1不在[l,r]内,则ai单独自己是一段
所以对于i来说,i位置存pos[ai+1],
查询[l,r]内pos值位于[l,r]内的数的个数,再用总段数减去即可,
由于都是[1,n]范围,则用主席树
代码1
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,l,r,a[N],pos[N];
int root[N],ls[50*N],rs[50*N],sum[50*N],c;
int lasans;
void upd(int l,int r,int &cur,int las,int pos){
cur=++c;
ls[cur]=ls[las];rs[cur]=rs[las];
sum[cur]=sum[las]+1;
if(l==r)return;
int mid=(l+r)/2;
if(pos<=mid)upd(l,mid,ls[cur],ls[las],pos);
else upd(mid+1,r,rs[cur],rs[las],pos);
}
int ask(int l,int r,int cur,int las,int ql,int qr){
if(ql<=l&&r<=qr)return sum[cur]-sum[las];
int ans=0;
int mid=(l+r)/2;
if(ql<=mid)ans+=ask(l,mid,ls[cur],ls[las],ql,qr);
if(qr>mid)ans+=ask(mid+1,r,rs[cur],rs[las],ql,qr);
return ans;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
pos[a[i]]=i;
}
for(int i=1;i<=n;++i){
//printf("%d: pos:%d\n",i,pos[a[i]+1]);
upd(0,n,root[i],root[i-1],pos[a[i]+1]);
}
scanf("%d",&m);
while(m--){
scanf("%d%d",&l,&r);
l^=lasans;r^=lasans;
if(l<1 || l>r || r>n)continue;
printf("%d\n",lasans=((r-l+1)-ask(0,n,root[r],root[l-1],l,r)));
}
return 0;
}
代码2
my学长的代码,
感觉这个主席树写法,更像线段树一点
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define fi first
#define se second
#define mp make_pair
#define pb push_back
const int N=110000;
const int M=4100000;
const LL mod=1e9+7;
int n,m;
int a[N],lastans;
int rk[N],root[N];
int ch[M][2],pos,sum[M];
int copy(int x)
{
pos++;
ch[pos][0]=ch[x][0];
ch[pos][1]=ch[x][1];
sum[pos]=sum[x];
return pos;
}
void add(int k,int l,int r,int x)
{
if (l==r) {sum[k]++;return;}
int mid=(l+r)/2;
if (x<=mid) add(ch[k][0]=copy(ch[k][0]),l,mid,x);
else add(ch[k][1]=copy(ch[k][1]),mid+1,r,x);
sum[k]=sum[ch[k][0]]+sum[ch[k][1]];
}
int ask(int k,int l,int r,int a,int b)
{
if (!k) return 0;
if (a==l&&b==r) return sum[k];
int mid=(l+r)/2;
if (b<=mid) return ask(ch[k][0],l,mid,a,b);
else if (a>mid) return ask(ch[k][1],mid+1,r,a,b);
else return ask(ch[k][0],l,mid,a,mid)+ask(ch[k][1],mid+1,r,mid+1,b);
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
rk[a[i]]=i;
}
root[0]=pos=1;
for (int i=1;i<=n;i++)
{
root[i]=copy(root[i-1]);
if (a[i]<n) add(root[i],1,n,rk[a[i]+1]);
}
scanf("%d",&m);
while (m--)
{
int l,r;
scanf("%d %d",&l,&r);
l^=lastans,r^=lastans;
if (l>r||l<1||r>n) continue;
int ans=r-l+1;
ans-=ask(root[r],1,n,l,r)-ask(root[l-1],1,n,l,r);
printf("%d\n",ans);
lastans=ans;
}
return 0;
}