【luogu P5161】WD与数列(SA)(单调栈)

WD与数列

题目链接:luogu P5161

题目大意

给你一个序列,问你有多少对区间,长度相同,没有相交部分,而且一个区间里面所有数同时加上某个数可以变成另一个区间。

思路

首先发现它要加数那个很难处理。
那考虑一个事情其实就是直接差分一下,就变成要两个一样了。

然后再考虑,发现不能相交的条件在差分之后你就不能相邻了,你要至少隔一个数。
而且因为差分,长度为 1 的全部都被你弄掉了,要单独加上。
然后考虑怎么求两两 LCP 的和。

其实可以直接 SAM 上 rush,也可以用 SA。
用 SA 的话可以这样,用一个单调栈,因为你 LCP 在 SA 上是 height 数组的最小值,那就是所有区间的最小值的和,用单调栈维护最小值就可以。
就维护当前点作为右端点,所有左端点形成的区间的答案,然后维护这这个,每次把这个加入答案就可以。

但是你这样没有处理重合,于是考虑处理重合的部分减去。
考虑一个东西,你枚举差的位置 len
考虑用关键点,我们把编号为 len 的倍数的都标记。
然后对于一个相邻的关键单 k,k+len,我们求 x[klen+1,k],然后 y=x+len
会发现你要重合至少长度要是 len,那一定会经过 k 这个点。
所以合法的 xlcs(k,k+len)k
然后再求出 lcp(k,k+len),发现右边能匹配的其实固定了,所以左端点右移会使得能匹配的长度少 1,所以是等差序列求和,算一下就搞定了。
(记得算上我们预先有的 len

代码

#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; const int N = 3e5 + 100; int n, s[N], a[N], b[N], bn, log2_[N]; ll ans; struct SA { int sa[N], rnk[N], x[N], y[N], fir[N], minn[N][21], height[N]; int tong[N]; void Sort(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]; } void Init() { 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]); } int LCP(int x, int y) { x = rnk[x]; y = rnk[y]; if (x > y) swap(x, y); x++; int k = log2_[y - x + 1]; return min(minn[x][k], minn[y - (1 << k) + 1][k]); } int sta[N], tot; void work() { 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) return 1ll * (R - L + 1) * (L - l + 1 + R - l + 1) / 2; return 0; } int main() { 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); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_P5161.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(102)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示