QOJ 7364 回文

首先考虑到这个询问是一个“截取”的形式,所以如果只保留下最长的一些回文串是做不了的。
于是就应该需要维护好每一个回文串的信息,便可以考虑 manacher。

接下来涉及到的下标都默认是经过 manacher 处理后的。
考虑已经对于每个 \(i\) 知道了 \(p_i\)\([i - p_i, i + p_i]\) 这一段为以 \(i\) 为中心的最长回文串)。
接下来对于询问 \([l, r]\) 该怎么处理。

那么此时就可以考虑每个 \(i\in [l, r]\) 对答案的贡献,那么一共有 \(3\) 个限制:是回文;左边界不过 \(l\);右边界不过 \(r\)
\(ans_i = \min\{p_i, i - l, r - i\}\)\(ans = \max\limits_{i = l}^r \{ans_i\}\)

如果考虑直接拆这个偏序关系的话会很难维护,于是考虑其他的做法。
考虑 \({ans_i}\)\({\min}\) 和最后 \({ans}\)\({\max}\) 这两个关系
因为 \(ans_i = \min\{p_i, i - l, r_i\}\),那么也就可以得到 \(p_i, i - l, r - i\ge ans_i\)
那么因为 \(ans = \max\{ans_i\}\),那就说明存在 \(ans_i\ge ans\)(这里只能取等),那就推出了 \(i\in [l, r]\)\(p_i, i - l, r - i\ge ans\)

于是发现在刚刚的讨论中找到了一个 \({ans}\) 合法的条件
于是就可以考虑二分 \(ans'\),判定这个 \(ans'\) 是否合法(\(ans\) 是否 \(\ge ans'\))。

此时再来回看判定条件:存在 \(i\in [l, r], p_i, i - l, r - i\ge ans'\)
那么此时有三个限制还是不好做,所以考虑利用 \({ans'}\) 便捷的直接满足一些限制
对于这个式子,如果来满足 \(p_i\) 这个限制,那么对于 \(i - l, r - i\) 还是不太方便,当然也是可以做的(整体二分 / 主席树)。
因为本身询问就是个区间 \([l, r]\),所以自然的,如果接下来考虑的也是个区间就好了。所以可以考虑 \(i - l, r - i\ge ans'\) 这个限制,可以得到 \(l + ans'\le i\le r - ans'\)
于是此时就得到了 \(i\) 的一个合法的范围,限制就只剩下了一个 \(p_i\ge ans'\)
此时因为要解决的问题是存在性问题,所以可以直接贪心的考虑。具体来说,如果存在 \(p_i\ge ans'\),那么一定有这个区间内的 \(\max \{p_i\}\ge ans'\)
于是整个判定问题被推导成了一个区间最值问题。

因为一共有 \(n\) 个元素,\(q\log n\) 次查最值,可以用 st 表维护来平衡复杂度,做到 \(\mathcal{O}((n + q)\log n)\)

#include<bits/stdc++.h>
constexpr int maxn = 1e6 + 10;
int n; char str[maxn], s[maxn];
int p[maxn];
int mx[21][maxn];
int main() {
   scanf("%s", str + 1);
   n = strlen(str + 1);
   s[0] = '$', s[1] = '#';
   for (int i = 1; i <= n; i++) {
      s[i * 2] = str[i], s[i * 2 + 1] = '#';
   }
   s[n * 2 + 2] = '&';
   n = n * 2 + 1;
   for (int i = 1, mid = 0, mxr = 0; i <= n; i++) {
      if (i <= mxr) {
         p[i] = std::min(mxr - i, p[mid * 2 - i]);
      }
      while (s[i - p[i] - 1] == s[i + p[i] + 1]) p[i]++;
      if (i + p[i] > mxr) {
         mid = i, mxr = i + p[i];
      }
   }
   memcpy(mx[0], p, sizeof(mx[0]));
   for (int i = 1; i < 21; i++) {
      for (int l = 1, r = 1 << i; r <= n; l++, r++) {
         mx[i][l] = std::max(mx[i - 1][l], mx[i - 1][l + (1 << i - 1)]);
      }
   }
   auto query =
      [&](int l, int r) {
         int k = std::__lg(r - l + 1);
         return std::max(mx[k][l], mx[k][r - (1 << k) + 1]);
      };
   int Q; scanf("%d", &Q);
   for (int l, r; Q--; ) {
      scanf("%d%d", &l, &r);
      int sl = 1, sr = r - l + 1, ans = 1;
      l = l * 2 - 1, r = r * 2 + 1;
      while (sl <= sr) {
         int mid = sl + sr >> 1;
         if (query(l + mid, r - mid) >= mid) {
            ans = mid, sl = mid + 1;
         } else {
            sr = mid - 1;
         }
      }
      printf("%d\n", ans);
   }
   return 0;
}
posted @ 2025-03-31 21:10  rizynvu  阅读(33)  评论(0)    收藏  举报