Description
给一个序列,支持区间轮换(即末尾的一个数挪到开头),查询区间等于k的有多少个。
Solution
分块,记
s
i
,
j
s_{i,j}
si,j为第
i
i
i块
j
j
j出现的次数,每个块维护一个链表,修改就是对
O
(
n
)
O(\sqrt n)
O(n)的块进行修改,用deque实现非常方便。
当然也可以打非旋转Treap,每个点维护权值线段树,合并的时候向上线段树合并。
也有离线做法,先用splay模拟一遍,把空位建出来,后按权值分类后用树状数组求即可。
Code
分块大法好
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<deque>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
using namespace std;
const int N=1e5+10,_N=350;
int be[N],tt=0;
int L[N],R[N];
int s[_N][N];
deque<int> b[N];
int c[N],t=0;
int a[N];
int main()
{
freopen("queue.in","r",stdin);
freopen("queue.out","w",stdout);
int n,m,_n;
scanf("%d %d",&n,&m);
for(_n=1;_n*_n<=n;_n++);
int t=0,tt=1;
L[1]=1;
fo(i,1,n){
scanf("%d",&a[i]);
be[i]=tt;
if(++t==_n) t=0,R[tt]=i,L[++tt]=i+1;
}
R[tt]=n;
fo(i,1,tt)
fo(j,L[i],R[i]) s[i][a[j]]++,b[i].push_back(a[j]);
while(m--){
int op,l,r;
scanf("%d %d %d",&op,&l,&r);
if(op==1){
if(be[l]==be[r]){
int p=be[l],w=b[p].at(r-L[p]);
b[p].erase(b[p].begin()+r-L[p]);
b[p].insert(b[p].begin()+l-L[p],w);
continue;
}
int p=be[l],q=be[r];
int w=b[q].at(r-L[q]);
b[q].erase(b[q].begin()+r-L[q]),s[q][w]--;
b[p].insert(b[p].begin()+l-L[p],w),s[p][w]++;
fo(i,p,q-1){
int w=b[i].back();
b[i].pop_back(),b[i+1].push_front(w);
s[i][w]--,s[i+1][w]++;
}
}
else{
int k;
scanf("%d",&k);
int ans=0;
if(be[l]==be[r]){
int p=be[l];
fo(i,l,r) ans+=b[p].at(i-L[p])==k;
printf("%d\n",ans);
continue;
}
int p=be[l],q=be[r];
fo(i,l,R[p]) ans+=b[p].at(i-L[p])==k;
fo(i,L[q],r) ans+=b[q].at(i-L[q])==k;
fo(i,p+1,q-1) ans+=s[i][k];
printf("%d\n",ans);
}
}
}