题目简述
小 C 把她标号从1到n的n只兔子排成长长的一排,来给他们喂胡萝卜吃。 第 i 只兔子的颜色是 a_i
小 C 想知道在区间 [l_j,r_j] 里有多少只颜色为 c_j的兔子。
有时编号为 x_j 和 x_j+1的两只兔子会交换位置
输入格式
输入第 1 行两个正整数 n,m。
输入第 2 行 n 个正整数,第 i 个数表示第 i 只兔子的颜色 a_i
输入接下来 m 行,每行为以下两种中的一种:
“
1
l
j
r
j
c
j
“1\ l_j\ r_j\ c_j
“1 lj rj cj ” :询问在区间
[
l
j
,
r
j
]
[l_j,r_j]
[lj,rj]里有多少只颜色为
c
j
c_j
cj的兔子
“
2
x
j
”
“2\ x_j”
“2 xj”:
x
j
x_j
xj和
x
j
+
1
x_j+1
xj+1两只兔子交换了位置。
n , m ≤ 3 ∗ 1 0 5 n,m \leq 3*10^5 n,m≤3∗105
输出格式
输出到标准输出中。
对于每个 1 操作,输出一行一个正整数,表示你对于这个询问的答案。
题目分析
首先说一下官方正解是vector存每个颜色出现位置,然后每次询问在对应vector里二分就行
但本人数据结构学傻了,第一眼带修改莫队,然后卡70pts,之后就怒写主席树,过了
所以这里贴主席树和莫队写法
按套路建主席树
每次交换x和x+1只用修改第x个线段树就行了
// 主席树
const int maxn=400010;
int n,m;
int a[maxn],b[maxn];
int pos[maxn],cnt;
int rt[maxn<<6],ch[maxn<<6][2],sz;
int size[maxn<<6];
int rem[maxn];
int update(int pre,int ll,int rr,int x,int w)
{
int tt=++sz;
size[tt]=size[pre]+w;
ch[tt][0]=ch[pre][0]; ch[tt][1]=ch[pre][1];
int mid=ll+rr>>1;
if(ll<rr)
{
if(x<=mid) ch[tt][0]=update(ch[pre][0],ll,mid,x,w);
else ch[tt][1]=update(ch[pre][1],mid+1,rr,x,w);
}
return tt;
}
int query(int lft,int rht,int ll,int rr,int c)
{
if(ll==rr) return size[rht]-size[lft];
int mid=ll+rr>>1;
if(c<=mid) return query(ch[lft][0],ch[rht][0],ll,mid,c);
else return query(ch[lft][1],ch[rht][1],mid+1,rr,c);
}
int main()
{
n=read(); m=read();
for(int i=1;i<=n;++i)
a[i]=b[i]=read();
sort(b+1,b+1+n);
for(int i=1;i<=n;++i)
if(i==1||b[i]!=b[i-1])
pos[++cnt]=b[i];
for(int i=1;i<=n;++i)
{
rem[a[i]]=1;
a[i]=lower_bound(pos+1,pos+1+cnt,a[i])-pos;
rt[i]=update(rt[i-1],1,cnt,a[i],1);
}
for(int i=1;i<=m;++i)
{
int opt=read();
if(opt==1)
{
int ll=read(),rr=read(),c=read();
if(!rem[c])
{
printf("0\n");
continue;
}
c=lower_bound(pos+1,pos+1+cnt,c)-pos;
printf("%d\n",query(rt[ll-1],rt[rr],1,cnt,c));
}
else if(opt==2)
{
int loc=read();
rt[loc]=update(rt[loc],1,cnt,a[loc],-1); // 删掉原来的数
rt[loc]=update(rt[loc],1,cnt,a[loc+1],1); // 加上下一位的数
swap(a[loc],a[loc+1]);
}
}
return 0;
}
// 带修改莫队
const int maxn=300010;
int n,m,sz;
struct Query{ int ll,rr,c,pre,id;}q[maxn];
struct Change{ int pos;}change[maxn];
int cntq,cntc;
int a[maxn],cnt[maxn];
int ans[maxn];
bool cmp(Query a,Query b)
{
if(a.ll/sz!=b.ll/sz) return a.ll/sz<b.ll/sz;
if(a.rr/sz!=b.rr/sz)
{
if((a.ll/sz)&1) return a.rr<b.rr;
else return a.rr>b.rr;
}
return a.pre<b.pre;
}
void add(int idx){ cnt[a[idx]]++;}
void del(int idx){ cnt[a[idx]]--;}
void modify(int idx, Query q)
{
if(idx==q.ll-1)
{
--cnt[a[idx+1]];
++cnt[a[idx]];
}
else if(idx==q.rr)
{
--cnt[a[idx]];
++cnt[a[idx+1]];
}
swap(a[idx],a[idx+1]);
}
int main()
{
n=read(); m=read();
for(int i=1;i<=n;++i)
a[i]=read();
for(int i=1;i<=m;++i)
{
int opt=read();
if(opt==1)
{
q[++cntq].ll=read();
q[cntq].rr=read();
q[cntq].c=read();
q[cntq].pre=cntc;
q[cntq].id=cntq;
}
else if(opt==2){
change[++cntc].pos=read();
}
}
sz=pow(n,2.0/3);
sort(q+1,q+1+cntq,cmp);
int L=1,R=0, cur=0;
for(int i=1;i<=cntq;++i)
{
while(R<q[i].rr) add(++R);
while(R>q[i].rr) del(R--);
while(L<q[i].ll) del(L++);
while(L>q[i].ll) add(--L);
while(cur<q[i].pre) modify(change[++cur].pos, q[i]);
while(cur>q[i].pre) modify(change[cur--].pos, q[i]);
ans[q[i].id]=cnt[q[i].c];
}
for(int i=1;i<=cntq;++i)
printf("%d\n",ans[i]);
return 0;
}