其实可以直接 SAM 上 rush,也可以用 SA。
用 SA 的话可以这样,用一个单调栈,因为你 LCP 在 SA 上是 height 数组的最小值,那就是所有区间的最小值的和,用单调栈维护最小值就可以。
就维护当前点作为右端点,所有左端点形成的区间的答案,然后维护这这个,每次把这个加入答案就可以。
但是你这样没有处理重合,于是考虑处理重合的部分减去。
考虑一个东西,你枚举差的位置 len。
考虑用关键点,我们把编号为 len 的倍数的都标记。
然后对于一个相邻的关键单 k,k+len,我们求 x∈[k−len+1,k],然后 y=x+len。
会发现你要重合至少长度要是 len,那一定会经过 k 这个点。
所以合法的 x 是 lcs(k,k+len)∼k。
然后再求出 lcp(k,k+len),发现右边能匹配的其实固定了,所以左端点右移会使得能匹配的长度少 1,所以是等差序列求和,算一下就搞定了。
(记得算上我们预先有的 len)
代码
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#define ll long longusingnamespace std;
constint N = 3e5 + 100;
int n, s[N], a[N], b[N], bn, log2_[N];
ll ans;
structSA {
int sa[N], rnk[N], x[N], y[N], fir[N], minn[N][21], height[N];
int tong[N];
voidSort(int m, int *x, int *y){
for (int i = 1; i <= m; i++) tong[i] = 0;
for (int i = 1; i <= n; i++) tong[x[i]]++;
for (int i = 1; i <= m; i++) tong[i] += tong[i - 1];
for (int i = n; i >= 1; i--)
sa[tong[fir[i]]--] = y[i];
}
voidInit(){
for (int i = 1; i <= n; i++) x[i] = a[i];
for (int i = 1; i <= n; i++) y[i] = i;
for (int i = 1; i <= n; i++) fir[i] = x[y[i]];
Sort(bn, x, y);
int m = bn;
for (int j = 1; j < n; j <<= 1) {
int ynum = 0;
for (int i = n - j + 1; i <= n; i++)
y[++ynum] = i;
for (int i = 1; i <= n; i++)
if (sa[i] > j) y[++ynum] = sa[i] - j;
for (int i = 1; i <= n; i++) fir[i] = x[y[i]];
Sort(m, x, y);
swap(x, y);
int mm = 1;
x[sa[1]] = 1;
for (int i = 2; i <= n; i++)
if (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + j] == y[sa[i - 1] + j]) x[sa[i]] = mm;
else x[sa[i]] = ++mm;
m = mm;
if (m == n) break;
}
int k = 0, j;
for (int i = 1; i <= n; i++) rnk[sa[i]] = i;
for (int i = 1; i <= n; i++) {
if (rnk[i] == 1) {height[rnk[i]] = k; continue;}
if (k) k--;
j = sa[rnk[i] - 1];
while (a[i + k] == a[j + k] && i + k <= n && j + k <= n) k++;
height[rnk[i]] = k;
}
for (int i = 1; i <= n; i++) minn[i][0] = height[i];
for (int i = 1; i <= 20; i++)
for (int j = 1; j + (1 << i) - 1 <= n; j++)
minn[j][i] = min(minn[j][i - 1], minn[j + (1 << (i - 1))][i - 1]);
}
intLCP(int x, int y){
x = rnk[x]; y = rnk[y];
if (x > y) swap(x, y); x++;
int k = log2_[y - x + 1];
returnmin(minn[x][k], minn[y - (1 << k) + 1][k]);
}
int sta[N], tot;
voidwork(){
tot = 0; sta[++tot] = 1; ll now = 0;
for (int i = 2; i <= n; i++) {
while (tot > 1 && height[sta[tot]] > height[i]) {
now -= 1ll * height[sta[tot]] * (sta[tot] - sta[tot - 1]);
tot--;
}
sta[++tot] = i; now += 1ll * height[sta[tot]] * (sta[tot] - sta[tot - 1]);
ans += now;
}
}
}T, Tf;
ll calc(int s, int t, int l){
int R = min(s, l) + t - 1, L = max(t, l);
if (L <= R) return1ll * (R - L + 1) * (L - l + 1 + R - l + 1) / 2;
return0;
}
intmain(){
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &s[i]);
log2_[0] = -1; for (int i = 1; i <= n; i++) log2_[i] = log2_[i >> 1] + 1;
ans = 1ll * n * (n + 1) / 2 - n;
n--;
for (int i = 1; i <= n; i++) a[i] = s[i + 1] - s[i], b[i] = a[i];
sort(b + 1, b + n + 1); bn = unique(b + 1, b + n + 1) - b - 1;
for (int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + bn + 1, a[i]) - b;
T.Init(); T.work();
reverse(a + 1, a + n + 1); Tf.Init();
reverse(a + 1, a + n + 1);
for (int k = 1; k <= n; k++)
for (int r = k + k; r <= n; r += k) {
int l = r - k; ans -= calc(Tf.LCP(n - r + 1, n - l + 1), T.LCP(l, r), k);
}
printf("%lld", ans);
return0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现