7.9 独居日 莫队相关
7.9 独居日
每当宿舍里只有我一个人的时候,就要与蚊子大战3个小时...脑壳疼...
后缀数组
先背代码:
int n;
char str[1000010];
int sa[1000010], SA[1000010], rk[1000010], RK[1000010], bac[1000010], alp;
int main() {
cin >> str + 1;
n = strlen(str + 1);
alp = 256;
for (int i = 0; i <= alp; ++i)
bac[i] = 0;
for (int i = 1; i <= n; ++i)
bac[str[i]] ++;
for (int i = 1; i <= alp; ++i)
bac[i] += bac[i - 1];
for (int i = 1; i <= n; ++i)
sa[bac[str[i]]--] = i;
for (int i = 1; i <= n; i++)
rk[sa[i]] = rk[sa[i - 1]] + (str[sa[i]] != str[sa[i - 1]]);
for (int p = 1; p <= n; p <<= 1) {
for (int i = 1; i <= n; ++i)
bac[rk[sa[i]]] = i;
for (int i = n; i >= 1; --i)
if (sa[i] > p)
SA[bac[rk[sa[i] - p]]--] = sa[i] - p;
for (int i = n; i > n - p; --i)
SA[bac[rk[i]] --] = i;
#define cmp(x, y) (rk[x] != rk[y] || rk[x + p] != rk[y + p])
for (int i = 1; i <= n; ++i)
RK[SA[i]] = RK[SA[i - 1]] + cmp(SA[i], SA[i - 1]);
for (int i = 1; i <= n; ++i)
sa[i] = SA[i], rk[i] = RK[i];
if (rk[sa[n]] >= n)
break;
}
for (int i = 1; i <= n; ++i)
cout << sa[i] << " ";
return 0;
}
本质不同的子串个数
回到我们的话题。我们还有一个数组没求,那就是height。height要怎么求呢?其实很简单,因为有性质:height[rank[i]] ≥height[rank[i−1]]−1。
证明: 如果 height[rank[i−1]] = 0 显然成立,否则在 i−1 这个后缀与它前驱的 LCP 的基础上去掉第一个字符,就找到了一个后缀与 i 这个后缀的 LCP≥height[rank[i−1]]−1。
于是我们能线性求height。
h[i] = lcp(sa[i], sa[i - 1])
带修莫队
这样的一种排序方法[左块增右块增时间增]
左端点在同一块并且右端点在同一块的,按照时间递增排序;
否则,左端点在同一块的,按照右端点所在块递增排序;
否则,按照左端所在块递增排序。
最优块长
不过胡试了几组,发现当按照[奇偶块右端点迂回增减]排序时,块长等于 \(n^{\frac{2}{3}}\)的时候得分高(指P1903 [国家集训队]数颜色 / 维护队列);当按照[左块增右块增时间增]排序时,块长等于 \(\sqrt{n * m}\) 得分高。以及块长2000对这两种排序方式都比较适用。
练习 P1494 [国家集训队]小Z的袜子
只需要记录当前区间内的袜子总数Sum
和每种袜子的数量colnum[n]
。
总的选择方案数即为\(Sum * (Sum - 1) / 2\),合法的选择方案数即为\(\sum (colnum[i] * (colnum[i] - 1) / 2)\)。
然后正常跑莫队即可,排序用奇偶块增减迂回排序。
注意
- 分子分母只要一个为零就要特判,不能约分了,直接答案存0/1。这里卡了好久
代码
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
const int N = 50006;
int n, m, a[N], num[N], bel[N], t, L, R, colnum[N];
ll Sum, sum0, ss;
struct P {
int id, l, r;
} p[N];
ll ans, Ans[N][2], g;
bool cmp(P A, P B){
return bel[A.l] == bel[B.l] ? (bel[A.l] & 1 ? A.r < B.r : A.r > B.r) : bel[A.l] < bel[B.l];
}
bool cmp0(P a, P b) {
return a.r < b.r;
}
void work(const int l, const int r){
for(int i = l; i <= r; ++i){
colnum[a[i]]++; Sum++;
}
for(int i = 1; i <= n; ++i) if(colnum[i] > 0) sum0 += 1ll * colnum[i] * (colnum[i] - 1) / 2;
return ;
}
void Add(const int Pos){
Sum++;
if(colnum[a[Pos]] > 0) sum0 -= 1ll * colnum[a[Pos]] * (colnum[a[Pos]] - 1) / 2;
colnum[a[Pos]]++;
sum0 += 1ll * colnum[a[Pos]] * (colnum[a[Pos]] - 1) / 2;
return;
}
void Del(const int Pos){
Sum--;
sum0 -= 1ll * colnum[a[Pos]] * (colnum[a[Pos]] - 1) / 2;
colnum[a[Pos]]--;
if(colnum[a[Pos]] > 0) sum0 += 1ll * colnum[a[Pos]] * (colnum[a[Pos]] - 1) / 2;;
return;
}
ll gcd(ll a, ll b) {
return b ? gcd(b, a % b) : a;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= m; i++) {
scanf("%d %d", &p[i].l, &p[i].r);
p[i].id = i;
}
t = pow(n, 1.0 / 3); t *= t;
if(t < 10) t = 10;
for(int i = 1; i <= n; ++i) bel[i] = i / t + 1;
sort(p + 1, p + m + 1, cmp);
L = p[1].l; R = p[1].r;
work(L, R);
for (int i = 1; i <= m; i++) {
while(L > p[i].l) Add(--L);
while(R < p[i].r) Add(++R);
while(L < p[i].l) Del(L++);
while(R > p[i].r) Del(R--);
if(Sum == 0 || sum0 == 0){
Ans[p[i].id][0] = 0;
Ans[p[i].id][1] = 1;
}
else{
ss = Sum * (Sum - 1) / 2;
g = gcd(sum0, ss);
Ans[p[i].id][0] = sum0 / g;
Ans[p[i].id][1] = ss / g;
}
}
for (int i = 1; i <= m; i++)
printf("%lld/%lld\n", Ans[i][0], Ans[i][1]);
return 0;
}
本文来自博客园,作者:咕咕坤,转载请注明原文链接:https://www.cnblogs.com/GuguKun/p/14992560.html