• [NOI2016] 优秀的拆分 题解


    [NOI2016] 优秀的拆分 题解

    link

    题意

    T 组询问,每组一个字符串 s

    s 所有字串分成 AABB 的方案数之和。 A,B 为非空串。

    题解

    fi 为一 i 结尾的 AA 串数量,gi 为一 i 结尾的 AA 穿数量。

    ans=fi×gi+1

    考虑求 f,g ,用后缀数组。需要多反转后建一个,用于求 lcs

    枚举 A 的长度 w ,之后所有相隔 w 的小标是对应的。

    w 倍数处设置关键点,则一个 A 最少经过一个关键点。

    枚举相邻的关键点 l,r ,求 lcp=lcp(l,r),lcs=lcs(l,r)

    lcp+lcs 为经过两个关键点且开头距离为 w 的最长公共子串。

    如果 lcp+lcsw 则存在合法的 AA 串,相当于一个区间加,用差分搞一下

    如图,黑色部分任意一个位置为起点都可以形成 AA 串,结尾同理

    最终复杂度为一个调和级数,就是 O(nlogn)

    代码

    1. #include
    2. #define clr(x) memset(x, 0, sizeof(x))
    3. using namespace std;
    4. const int N = 30005;
    5. char a[N];
    6. int n, lg[N];
    7. long long ff[N], gg[N];
    8. struct suf {
    9. int sa[N], rk[N], old[N], t[N], id[N], m, h[N], f[N][17];
    10. inline void rs() {
    11. for (int i = 1; i <= m; i++) t[i] = 0;
    12. for (int i = 1; i <= n; i++) ++t[rk[i]];
    13. for (int i = 1; i <= m; i++) t[i] += t[i - 1];
    14. for (int i = n; i >= 1; i--) sa[t[rk[id[i]]]--] = id[i], id[i] = 0;
    15. }
    16. inline int EQ(int x, int y, int k)
    17. { return old[x] == old[y] && old[x + k] == old[y + k]; }
    18. inline void bui() {
    19. clr(sa), clr(rk), clr(old), clr(t), clr(id), clr(h);
    20. memset(f, 10, sizeof(f));
    21. m = 200;
    22. for (int i = 1; i <= n; i++) rk[i] = a[i], id[i] = i;
    23. rs();
    24. for (int k = 1, p; k <= n; k <<= 1) {
    25. p = 0;
    26. for (int i = n - k + 1; i <= n; i++) id[++p] = i;
    27. for (int i = 1; i <= n; i++) if (sa[i] > k) id[++p] = sa[i] - k;
    28. rs(), memcpy(old, rk, sizeof(rk)), p = 0;
    29. for (int i = 1; i <= n; i++) rk[sa[i]] = EQ(sa[i], sa[i - 1], k) ? p : ++p;
    30. if (p == n) break;
    31. m = p;
    32. }
    33. for (int i = 1, j, k = 0; i <= n; h[rk[i++]] = k)
    34. for (k ? --k : 0, j = sa[rk[i] - 1]; a[i + k] == a[j + k]; k++);
    35. for (int i = 1; i <= n; i++) f[i][0] = h[i];
    36. for (int j = 1; j <= 15; j++)
    37. for (int i = 1; i + (1 << j - 1) <= n; i++)
    38. f[i][j] = min(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
    39. }
    40. inline int lcp(int x, int y) {
    41. x = rk[x], y = rk[y];
    42. if (x == y) return n - sa[x] + 1;
    43. if (x > y) x ^= y ^= x ^= y; x++;
    44. int k = lg[y - x + 1];
    45. return min(f[x][k], f[y - (1 << k) + 1][k]);
    46. }
    47. };
    48. suf A, B;
    49. inline void sol() {
    50. memset(ff, 0, sizeof(ff));
    51. memset(gg, 0, sizeof(gg));
    52. A.bui();
    53. reverse(a + 1, a + n + 1);
    54. B.bui();
    55. for (int w = 1; w <= n / 2; w++)
    56. for (int l = w, r; l + w <= n; l += w) {
    57. r = l + w;
    58. int lcp = min(w, A.lcp(l, r));
    59. int lcs = min(w - 1, B.lcp(n - l + 2, n - r + 2));
    60. if (lcp + lcs >= w) {
    61. int t = lcp + lcs - w + 1;
    62. ff[r + lcp - t]++, ff[r + lcp]--;
    63. gg[l - lcs]++, gg[l - lcs + t]--;
    64. }
    65. }
    66. for (int i = 1; i <= n; i++) ff[i] += ff[i - 1], gg[i] += gg[i - 1];
    67. long long ans = 0;
    68. for (int i = 1; i < n; i++) ans += ff[i] * gg[i + 1];
    69. printf("%lld\n", ans);
    70. }
    71. int main() {
    72. for (int i = 2; i <= 30000; i++) lg[i] = lg[i >> 1] + 1;
    73. int Ti;
    74. scanf("%d", &Ti);
    75. while (Ti--) {
    76. scanf("%s", a + 1);
    77. n = strlen(a + 1);
    78. sol();
    79. }
    80. }
  • 相关阅读:
    SOFAJRaft与BRaft:打造稳定高效的分布式一致性架构
    数据库 备份和恢复
    37-Java方法的内存原理
    面向混合型数据集自适应聚类的差分隐私保护算法
    《Qt-OpenGL系列编程》课程学习记录(10):冯氏光照模型
    Rabbitmq小书
    企业如何走出固定资产管理的困境?
    ubuntu下载conda
    自定义嵌入一个tomcat | 可设置自定义html模板,设置请求路径
    java ssm德育分活动报名系统springboot+vue
  • 原文地址:https://blog.csdn.net/KonjakuLAF/article/details/126854268