Before it
来当一回教练的题解翻译家,平衡树这种高级算法自然是不会的,所以详细讲一下STL。
成为蒟蒻(也就是自己)的福音。
Solution
我们观察到题目要求“把第 \(r\) 个数插入在第 \(l\) 个数之前,其他数顺序不变“,使用 \(deque\) 的\(push\)_\(front\) 和 \(push\) _ $ back$ 操作可以轻而易举的实现。
现在考虑分块,对于每一块维护一个\(deque\) ,$q_{i,j} $ 表示第 \(i\) 块,第 \(j\) 位的值是多少,同时对于每一块开一个桶数组,\(tot_{i,j}\) 维护第 \(i\) 块有多少个 \(j\)。
这里详细讲一下如何处理操作。
-
初始化
请参考数列分块9题,维护每个块的开头,结尾,以及 \(i\) 在哪个块。(作者使用 \(pos\) 数组。
-
移动
对于在同一个块的情况,暴力修改即可。可以开一个栈辅助转移(当然啦你不开也可以。
对于不在同一个块的情况,求出左碎块和右碎块的大小,我们先把左碎块的值存到栈里,然后将其直接删去。然后找到被往前移的那个加到栈里,然后再讲左碎块填满,此时栈里还剩一个元素。
对于中间的整块,实际上也就是元素都往后移动一个,可以在这个块的 \(deque\) 数组里 \(push\)_ \(front\) 上一个块遗留下来的最后一个值,然后在把最后一个元素 \(pop\)_\(back\) 走,把它存到栈里转移到下一个块。
对于右碎块,我们先把右碎块里的值依次存入栈里,扔掉栈顶元素(也就是被往前移动的那个),最后再依次将右碎块补满即可。
注意在转移的过程中,注意 \(tot\) 也需要同步转移。
-
求出区间 \(l--r\) 中 \(k\) 的值
对于在同一个块的情况,直接暴力计算即可。
对于左右碎块,求出长度和起终点,暴力计算。这里注意端点的细节!(
作者写错这里调了很久...对于中间的块,计算桶的答案。
至此,本题得到解决。
时间复杂度 \(O(n\sqrt{n})\)。但是因为STL常数很大所以跑得很慢...
Code
代码
//分块题,并不是神秘线段树
//对于每个块维护一个deque 用来支持1操作
//维护一个桶,便于查询
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,m,blen,a[maxn],op,l,r,k,s[maxn];//s数组是手写栈,为了方便转移操作的
int pos[maxn],st[maxn],ed[maxn],tot[320][maxn];//tot[i][j]表示第i块有多少个j
deque<int> q[320]; //和vector类似,可以使用下标访问
void init(){
blen=sqrt(n);
int t=n/blen;
if(n%blen)t++;
for(int i=1;i<=t;i++){
st[i]=ed[i-1]+1;
ed[i]=ed[i-1]+blen;
}
ed[t]=n;
for(int i=1;i<=n;i++){
pos[i]=(i-1)/blen+1;
tot[pos[i]][a[i]]++;
q[pos[i]].push_back(a[i]);
}
}
void modify(int l,int r){
int top=0;
if(pos[l]==pos[r]){
int now=pos[l];
l-=st[now],r-=st[now],s[++top]=q[now][r];
for(int i=l;i<r;i++) s[++top]=q[now][i];
for(int i=r;i>=l;i--) q[now][i]=s[top--];
return;
}
int cnt1=ed[pos[l]]-l+1,cnt2=r-st[pos[r]]+1;
while(cnt1--){
int x=q[pos[l]].back();
s[++top]=x;
tot[pos[l]][x]--;
q[pos[l]].pop_back();
}
s[++top]=q[pos[r]][cnt2-1];//被往前移的那个
while(top>1){
int x=s[top];
q[pos[l]].push_back(x);
tot[pos[l]][x]++;
top--;
}
for(int i=pos[l]+1;i<=pos[r]-1;i++){//处理整块 往后移动一个
int x=s[top];
q[i].push_front(x);
tot[i][x]++,top--;
int y=q[i].back();
tot[i][y]--;
s[++top]=y;
q[i].pop_back();
}
while(cnt2--){
int x=q[pos[r]].front();
s[++top]=x;
tot[pos[r]][x]--,q[pos[r]].pop_front();
}
top--;
while(top){
int x=s[top];
q[pos[r]].push_front(x);
tot[pos[r]][x]++;
top--;
}
}
int query(int l,int r,int k){
int ans=0;
if(pos[l]==pos[r]){
int now=pos[l];
l-=st[now],r-=st[now];
for(int i=l;i<=r;i++){
if(q[now][i]==k) ans++;
}
return ans;
}
int cnt1=ed[pos[l]]-l+1,cnt2=r-st[pos[r]]+1;
//处理左碎块
for(int i=q[pos[l]].size()-cnt1;i<=q[pos[l]].size()-1;i++){
if(q[pos[l]][i]==k) ans++;
}
// //处理右碎块
for(int i=0;i<=cnt2-1;i++){
if(q[pos[r]][i]==k) ans++;
}
// cout<<"Aapwp"<<ans<<endl;
for(int i=pos[l]+1;i<=pos[r]-1;i++){
ans+=tot[i][k];
}
// cout<<"Aapwp"<<ans<<endl;
return ans;
}
int main(){
ios::sync_with_stdio(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
init();
cin>>m;
int lans=0;
while(m--){
cin>>op>>l>>r;
l=((lans+l-1)%n)+1;
r=((lans+r-1)%n)+1;
if(l>r) swap(l,r);
if(op==1){
modify(l,r);
}
else{
cin>>k;
k=((lans+k-1)%n)+1;
lans=query(l,r,k);
cout<<lans<<endl;
}
}
}