大意:给定n元素序列, 2种操作
- 将区间$[l,r]$循环右移1位
- 询问$[l,r]$中有多少个等于k的元素
现在给定q个操作, 输出操作2的询问结果, 强制在线
思路1: 分块
每个块内维护一个链表, 循环右移相当于删除一个元素, 再插入一个元素, 每个块内再维护一个桶统计元素个数即可
分块好久没写过了, 先放个分块大致流程
void init() {
//sqn是分块数, blo[i]是位置i所属块的编号
//L[i], R[i]是位置i所属块的左右边界
sqn = sqrt(n);
REP(i,1,n) {
blo[i] = (i-1)/sqn+1;
L[i] = (blo[i]-1)*sqn+1;
R[i] = blo[i]*sqn;
}
}
void work(int l, int r) {
if (blo[l]==blo[r]) {
//在一块直接暴力
return;
}
REP(i,l,R[l]) {
//第一块暴力
}
REP(i,blo[l]+1,blo[r]+1) {
//处理中间每个块
}
REP(i,L[r],r) {
//最后一块暴力
}
}
该题的代码如下. deque用的还是不熟练, 很简单的思路还是码了一个多小时.
#include <iostream>
#include <math.h>
#include <algorithm>
#include <cstdio>
#include <queue>
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;
typedef long long ll;
const int N = 1e5+10, M = 333;
int n, m, sqn;
int L[N], R[N], blo[N];
int sum[M][N];
deque<int> q[N];
void work(int l, int r) {
int x;
auto t = q[blo[r]].begin();
REP(i,L[r],r-1) ++t;
x = *t;
--sum[blo[r]][x];
q[blo[r]].erase(t);
t = q[blo[l]].begin();
REP(i,L[l],l-1) ++t;
++sum[blo[l]][x];
q[blo[l]].insert(t,x);
if (blo[l]==blo[r]) return;
x = q[blo[l]].back();
--sum[blo[l]][x];
q[blo[l]].pop_back();
REP(i,blo[l]+1,blo[r]-1) {
++sum[i][x];
q[i].push_front(x);
x = q[i].back();
--sum[i][x];
q[i].pop_back();
}
++sum[blo[r]][x];
q[blo[r]].push_front(x);
}
int query(int l, int r, int k) {
ll ans = 0;
if (blo[l]==blo[r]) {
REP(i,l,r) ans += (q[blo[l]][i-L[i]]==k);
return ans;
}
REP(i,l,R[l]) ans += (q[blo[l]][i-L[i]]==k);
REP(i,blo[l]+1,blo[r]-1) ans += sum[i][k];
REP(i,L[r],r) ans += (q[blo[r]][i-L[i]]==k);
return ans;
}
int main() {
scanf("%d", &n), sqn=sqrt(n);
REP(i,1,n) {
int t;
scanf("%d", &t);
blo[i]=(i-1)/sqn+1;
++sum[blo[i]][t];
q[blo[i]].push_back(t);
L[i]=(blo[i]-1)*sqn+1,R[i]=blo[i]*sqn;
}
scanf("%d", &m);
int ans = 0;
REP(i,1,m) {
int op, l, r, k;
scanf("%d%d%d", &op, &l, &r);
l = (l+ans-1)%n+1, r = (r+ans-1)%n+1;
if (l>r) swap(l,r);
if (op==1) work(l,r);
else {
scanf("%d", &k);
k = (k+ans-1)%n+1;
printf("%d\n", ans=query(l,r,k));
}
}
}
思路2: splay
先留着以后填吧......