我们考虑把一个本原平方串按左右坐标放在二维平面上,那每次询问就是二维数点。
那考虑到问题为什么难求,因为可能有重复的,考虑容斥掉(其实也没有什么好容斥的,就是你构造一种方法使得减剩一个)
那就是你考虑到相同的有两种可能:同一个 run 里面的,不同的 run 之间的。
先看同一个 run 里面的,那发现一定是形成一个斜率 45° 的线。
那就考虑从第二个点开始在它 y 轴的位置上一个点 x 轴的位置放一个 −1,那这样如果碰到了第二个必然也会碰到这个消去的。
然后你发现这个消去的也是斜线。
或者还有一种方法就是这里用的,你会发现它其实是两部分(就对于一个 2p),然后两部分之间会有,那我们就限制至多要一边的。
然后考虑不同的 run 之间的,就直接看哪个斜线是不是同一个(可以用 map 判),然后如果是就前面的最后一个跟后面的第一个之间像前面的方法一样放点,这个直接单独放即可。
然后至于斜线怎么二维偏序数点就拆成两个射线,然后神笔维护一下即可。
(至于维护可以看代码/fad)
代码
#include<map>#include<cstdio>#include<vector>#include<algorithm>#define ll long longusingnamespace std;
constint N = 2e5 + 1000;
int n, q, ans[N], ly[N], sta[N], m, fn;
char s[N];
structnode {
int l, r, id;
}qs[N];
structRUNS {
int l, r, p;
}runs[N];
structHASH {
constint di1 = 13331, di2 = 131;
constint mo1 = 1e9 + 7, mo2 = 1e9 + 9;
ll mi1[N], hash1[N], mi2[N], hash2[N];
voidstart(){
mi1[0] = mi2[0] = 1;
for (int i = 1; i <= n; i++) {
mi1[i] = mi1[i - 1] * di1 % mo1; mi2[i] = mi2[i - 1] * di2 % mo2;
hash1[i] = (hash1[i - 1] * di1 + s[i] - 'a' + 1) % mo1;
hash2[i] = (hash2[i - 1] * di2 + s[i] - 'a' + 1) % mo2;
}
}
pair <int, int> get(int l, int r) {
returnmake_pair((hash1[r] - hash1[l - 1] * mi1[r - l + 1] % mo1 + mo1) % mo1, (hash2[r] - hash2[l - 1] * mi2[r - l + 1] % mo2 + mo2) % mo2);
}
}H;
map <pair <int, int>, int> mp;
intgetl(int x, int y){
int l = 1, r = min(x, y), re = 0;
while (l <= r) {
int mid = (l + r) >> 1;
if (H.get(x - mid + 1, x) == H.get(y - mid + 1, y)) re = mid, l = mid + 1;
else r = mid - 1;
}
return re;
}
intgetr(int x, int y){
int l = 1, r = min(n - x + 1, n - y + 1), re = 0;
while (l <= r) {
int mid = (l + r) >> 1;
if (H.get(x, x + mid - 1) == H.get(y, y + mid - 1)) re = mid, l = mid + 1;
else r = mid - 1;
}
return re;
}
boolcmpS(int x, int y){
int pl = getr(x, y);
return s[x + pl] < s[y + pl];
}
voidLyndon(bool op){
ly[n] = n;
sta[0] = 0; sta[++sta[0]] = n + 1; sta[++sta[0]] = n;
for (int i = n - 1; i >= 1; i--) {
while (sta[0] > 1 && cmpS(i, sta[sta[0]]) == op) sta[0]--;
sta[++sta[0]] = i;
ly[i] = sta[sta[0] - 1] - 1;
}
}
voidslove(int x, int y){
int l = getl(x, y), r = getr(x, y);
if (l + r >= y - x + 1) runs[++m] = (RUNS){x - l + 1, y + r - 1, y - x};
}
boolcmp_run(RUNS x, RUNS y){
if (x.l != y.l) return x.l < y.l;
if (x.r != y.r) return x.r < y.r;
return x.p < y.p;
}
voidget_runs(){
for (int op = 0; op <= 1; op++) {
Lyndon(op);
for (int i = 1; i < n; i++) {
slove(i, ly[i] + 1);
}
}
sort(runs + 1, runs + m + 1, cmp_run);
}
voidInit(){
H.start();
get_runs();
int tmp = m; m = 0;
for (int i = 1; i <= tmp; i++)
if (runs[i].l != runs[i - 1].l || runs[i].r != runs[i - 1].r) {
runs[++m] = runs[i];
}
}
boolcmp_qr(node x, node y){
return x.r < y.r;
}
structSZSZ {
ll f[N], sum;
voidadd(int x, int y){
sum += y;
for (; x <= n + n; x += x & (-x)) f[x] += y;
}
ll ask(int x){
ll re = 0;
for (; x; x -= x & (-x)) re += f[x];
return re;
}
}T;
vector <pair<int, int> > ed[N];
vector <RUNS> run[N];
voidslove(){
sort(qs + 1, qs + q + 1, cmp_qr);
for (int x = 1; x <= m; x++) {
run[runs[x].r].push_back(runs[x]);
int l = runs[x].l, r = runs[x].r, p = runs[x].p;
for (int i = l + 2 * p - 1; i <= r; i++)
ed[i].push_back(make_pair(l, 2 * p));
}
int now = 1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < ed[i].size(); j++) { int l = ed[i][j].first, p = ed[i][j].second;
int pl = i - ((i - l + 1) / p * p) + 1;
pair <int, int> now = H.get(pl, i);
if (mp[now]) {
T.add(mp[now], -1);
mp[now] = 0;
}
}
while (now <= q && qs[now].r <= i) {
ans[qs[now].id] = T.sum - T.ask(qs[now].l - 1);
for (int j = 0; j < ed[i].size(); j++) {
int l = max(qs[now].l, ed[i][j].first);
int t = (qs[now].r - l + 1) / ed[i][j].second;
int ql = qs[now].r - ed[i][j].second / 2 + 1;//这个是内部相同的限制if (t) {
int lef = max(ql, t * ed[i][j].second + l - 1);
ans[qs[now].id] += (qs[now].r - lef + 1) * t;
if (lef > ql) ans[qs[now].id] += (lef - ql) * (t - 1);
}
}
now++;
}
for (int j = 0; j < run[i].size(); j++) { RUNS p = run[i][j];
for (int r = p.r - p.p + 1; r <= p.r; r++)
for (int l = r - 2 * p.p + 1; l >= p.l; l -= 2 * p.p) {
pair <int, int> now = H.get(l, r);
mp[now] = l; T.add(l, 1);
}
}
}
}
intmain(){
scanf("%d %d", &n, &q);
scanf("%s", s + 1);
for (int i = 1; i <= q; i++) {
scanf("%d %d", &qs[i].l, &qs[i].r); qs[i].id = i;
}
Init();
slove();
for (int i = 1; i <= q; i++) printf("%d\n", ans[i]);
return0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现