网上看了挺多题解了,要么上树状数组或者线段树,要么上的主席树,还有莫队加树状数组的。能用主席树这题真的就很简单了,但是笔试能现搓一个主席树我是真佩服。
所以在这里发一份不使用任何数据结构板子的代码,不需要太多算法基础,适合大众,莫队做法(貌似还没看到有人发?)
小美的彩带
彩带是由一条长度为 n 的彩带一直无限循环得到的,彩带的每一个位置都有一个颜色,用 a[i] 表示。a[ i ] = a[ j ] 表示 i 位置和 j 位置颜色相同。小美每次会在当前彩带(上次剪完剩下的)从左往右或从右往左剪一段长度为 x 的彩带,她想知道她每次剪下来的彩带有多少种颜色,剪q次。
n,q<=2e5,x<=1e9
思路:
x大于等于n的话答案就是彩带的颜色数量,所以x的真实范围其实是x<n。那么相当于多次询问,每次询问一个长度不超过n的子彩带内颜色数量。这个模型就很熟悉了,可以将询问离线下来处理,具体的形式是b[ i ] = { L , R , idx},L是剪下来的子彩带的左端点下标,R是右端点下标,idx是第几次操作。为了方便可以把彩带扩长一倍到2n,使1<=L<=n,n+1<=R<=2*n,这样任何一段子彩带都是连续的。
接下来就可以优雅的暴力了,我们将L分块,每一块的长度设为sqrt(n),也就是说0~sqrt(n)在一块,sqrt(n)~2*sqrt(n)在一块...,对于不在同一块的查询按照块号从小到大排序即可,块号相同的按照R从小到大排序,接下来直接暴力即可。
分块可以将单纯暴力的复杂度从n^2降为o(n*sqrt(n))的,证明我就贴oiwiki的了,有兴趣的可以看普通莫队算法 - OI Wiki
还有一个问题,我们如何o1的把询问区间从 [L,R] 转移到 [L-1,R],[L+1,R],[L,R-1],[L,R+1]也就是相邻的区间,可以假设我们用map存储了 [L,R]区间内每种颜色和对应的数量,那么只需要insert和erase就知道相邻的区间的颜色数量,但是map复杂度是logN的,因此我们可以先将a数组离散化(因为a[i]范围是1e9),再通过数组模拟map,并且手动维护map.size()即可,这样复杂度就会从n*sqrt(n)*log(n)变成n*sqrt(n)+nlog(n)
下面是代码,这里的块实测取sqrt(n),2*sqrt(n),sqrt(2*n)等常见的都能焯过去,不过1.5*sqrt(2*n)貌似快些,注意在分块排序的时候的细节
(弱弱第一次发文章,尽力讲了,希望没有算法基础不会数据结构的人也能够听懂)
#include <bits/stdc++.h>
using namespace std;
#define all(x) x.begin(),x.end()
int n, q, len, m, blo;
char op; int length;
int lisan[200005];
int a[400005], ans[200005];
int mp[200005], siz;
struct stuct {
int l, r, idx;
bool operator<(const stuct& x) const { //如果超时了可以试试这种
if (l / blo != x.l / blo) return l < x.l;
return (l / blo) & 1 ? r > x.r : r < x.r;
}
} b[200005];
int get(int v) { //离散的具体实现方式因人而异
return lower_bound(lisan+1, lisan+1+m, v) - lisan;
}
int main() {
scanf("%d %d", &n, &q);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
lisan[i] = a[i];
}
sort(lisan+1, lisan+1+n);
m = unique(lisan+1, lisan+1+n)-lisan;
--m;
blo = sqrt(2*n)*1.5;
for (int i = 1; i <= n; ++i) { //离散化
a[i] = get(a[i]);
a[i + n] = a[i];
}
scanf("\n");
int l = 1, r = 2*n, len = 0; //维护一下l,r才知道彩带边缘的下标
for (int i = 1; i <= q; ++i) {
scanf("%c %d\n", &op, &length);
if (length >= n) {
ans[i] = m;
length %= n;
if (op=='L') {
l += length;
if (l>n)l -= n;
}
else {
r -= length;
if (r<=n)r += n;
}
continue;
}
if (op == 'L') {
++len;
b[len].l = l, b[len].r = l+length-1, b[len].idx = i;
l += length;
if (l > n)l -= n;
}
else {
++len;
b[len].l = r-length+1, b[len].r = r, b[len].idx = i;
r -= length;
if (r<=n)r += n;
}
}
sort(b+1, b+1+len);
l = 0, r = 0;
for (int i = 1; i<=len; ++i) {
while (r<b[i].r) {
++r;
if (mp[a[r]]==0)siz++;
mp[a[r]]++;
}
while (r>b[i].r) {
mp[a[r]]--;
if (mp[a[r]]==0)siz--;
--r;
}
while (l<b[i].l) {
mp[a[l]]--;
if (mp[a[l]]==0)--siz;
++l;
}
while (l>b[i].l) {
--l;
if (mp[a[l]]==0)++siz;
mp[a[l]]++;
}
ans[b[i].idx] = siz;
}
for (int i = 1; i <= q; ++i) printf("%d\n", ans[i]);
return 0;
}