题目链接:
看到题第一眼,线段树?树状数组?主席树(蒟蒻不会)全冒出来?
可是仔细看题发现这是道水题
我们先把每种颜色读入
预处理出每种颜色所在位置的排序
拿样例举例:
1 2 3 2 3 3我们第一次初始化为b数组:
第一种颜色下标有:1
第二种颜色下标有:2,4
第三种颜色下标有:3,5,6
故b数组为1,2,4,3,5,6
用另两个数组记录每种颜色的开头和结尾
对于查询操作我们考虑二分:找到第一个比l大的数的下标L,找到最后一个比r小的数的下标R,R-L+1即为所求.
对于修改操作:
我们发现它们的相对位置是不变的:
拿上面的a数组样例2,3交换说明:
1,当这两种颜色不同时
容易证明这两个数在原来各自颜色的相对位置是不变的,所以直接暴力修改a数组就可以了
2, 当这两种颜色相同时改和不改一样,所以不用操作
上代码
---------------------------------------------------------------------------------分割线------------------------------------------------------------------------------------------------------------------------------------------
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
const int N=300010;
int n,m,st[N],en[N],tot[N],tot1[N];
int a[N],xu[N],b[N];
void pre()
{
int i,j,k,last=0;
for(i=1;i<N;++i)
if(tot[i]!=0)
{
st[i]=last+1,en[i]=st[i]+tot[i]-1;
last=en[i];tot1[i]=st[i]-1;
}
for(i=1;i<=n;++i)
b[++tot1[a[i]]]=i,xu[i]=tot1[a[i]];
}
void find(int l,int r,int c)
{
int i,j,ans,ans1,mid;
ans=0;ans1=0;
i=st[c];j=en[c];
while(i<=j)
{
mid=(i+j)/2;
if(b[mid]>=l) j=mid-1,ans1=mid;
else i=mid+1;
}
i=st[c];j=en[c];
while(i<=j)
{
mid=(i+j)/2;
if(b[mid]<=r) i=mid+1,ans=mid;
else j=mid-1;
}
if(ans!=0&&ans1!=0)
printf("%d\n",ans-ans1+1);
else printf("0\n");
}
void work()
{
int i,j,k,t,t1,t2,t3;
for(i=1;i<=m;++i)
{
scanf("%d",&j);
if(j==2)
{
scanf("%d",&k);
t=xu[k];t1=xu[k+1];
if(a[k]!=a[k+1])
{
b[t1]--;b[t]++;
t2=xu[k];xu[k]=xu[k+1];xu[k+1]=t2;
t2=a[k];a[k]=a[k+1];a[k+1]=t2;
}
}
else
{
scanf("%d%d%d",&t,&t1,&t2);
find(t,t1,t2);
}
}
}
int main()
{
freopen("csy.in","r",stdin);
freopen("csy.out","w",stdout);
int i,j,k;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i)
{
scanf("%d",&a[i]);
tot[a[i]]++;
}
pre();
work();
return 0;
}