LOJ #6436. 「PKUSC2018」神仙的游戏(字符串+NTT)

题面

LOJ #6436. 「PKUSC2018」神仙的游戏

题解

参考 yyb 的口中的长郡最强选手 租酥雨大佬的博客 ...

一开始以为 通配符匹配 就是类似于 BZOJ 4259: 残缺的字符串 这样做 .

把通配符设成 0 然后 . 别的按 ASCII 码 给值 , 最后把他写成式子的形式 ...

后来发现太年轻了 qwq

先要做这题 , 那么先发现性质咯 :

存在一个长度为 lenborder 当且仅当对于 i[1,nlen]s[i]=s[nlen+i]
或者这样说,把所有位置在模 nlen 意义下分组,同一组里的 01 要全部相同。

这个容易理解 :

如图所示 img

上下 棕色 分别为前后缀可以匹配上的部分 , 红色箭头 表示向后移动 nlen 个 长度 .

可以把 棕色 抽象成向右平移了 nlen 个位置 , 相对位置上的数仍然不变 .

有了这个性质 , 我们可以对于答案进行判断了 .

也就是对于每一对 01 , 假设 0 所处位置为 i , 1 位置为 j . x=|ij|

那么对于任意的 y|xy 都是不可行的 , 这就意味着 borderny 的时候也不可行 .

xmody=0ij(modx)

ij(mody) , 所以无法分到同一组 .

那么我们只要求出所有可能的 x 然后用 O(nlnn) 枚举倍数判断一个数 , 就可以了 .

暴力枚举 0,1 对可以拿满暴力分 ....

正解的话只要优化枚举的复杂度 就行了 .

不难发现原式是 x=|ij| 我们可以构造卷积

A=i=0n[stri=0]xi,B=i=0n[strni=1]xi,AB=C .

按前面的假设 , 若有 0i , 1j . 那么 |ij| 必是不合法的 .

C 中我们发现 ij 对应的是 n+(ij) , 那么就可以直接做完了 .

NTT 优化卷积就行了 . 总时间复杂度 O(nlogn) .

总结

可以利用 border 与 循环串 能互相转化的性质做一些字符串题。

代码

#include <bits/stdc++.h> #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Set(a, v) memset(a, v, sizeof(a)) using namespace std; inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;} inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;} inline int read() { int x = 0, fh = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1; for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48); return x * fh; } void File() { #ifdef zjp_shadow freopen ("6436.in", "r", stdin); freopen ("6436.out", "w", stdout); #endif } const int N = (5e5 + 1e3) * 4; typedef long long ll; const int Mod = 998244353; ll fpm(ll x, int power) { ll res = 1; for (; power; power >>= 1, (x *= x) %= Mod) if (power & 1) (res *= x) %= Mod; return res; } const int Len = (1 << 20) + 5; struct Number_Theoretical_Transform { ll pow3[Len], invpow3[Len]; void Init(int maxn) { for (int i = 1; i <= maxn; i <<= 1) { pow3[i] = fpm(3, (Mod - 1) / i); invpow3[i] = fpm(pow3[i], Mod - 2); } } int n; int rev[Len]; void NTT(ll P[], int opt) { For (i, 0, n - 1) if (i < rev[i]) swap(P[i], P[rev[i]]); for (int i = 2; i <= n; i <<= 1) { int p = i >> 1; ll Wi = opt == 1 ? pow3[i] : invpow3[i]; for (int j = 0; j < n; j += i) { ll x = 1; for (int k = 0; k < p; ++ k, (x *= Wi) %= Mod) { ll u = P[j + k], v = x * P[j + k + p] % Mod; P[j + k] = (u + v) % Mod; P[j + k + p] = (u - v + Mod) % Mod; } } } if (opt == -1) { ll invn = fpm(n, Mod - 2); For (i, 0, n - 1) (P[i] *= invn) %= Mod; } } void Mult(ll A[], ll B[], int na, int nb) { Init(1 << 20); int m = na + nb, cnt = 0; for (n = 1; n <= m; n <<= 1) ++ cnt; For (i, 0, n - 1) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (cnt - 1)); NTT(A, 1); NTT(B, 1); For (i, 0, n - 1) (A[i] *= B[i]) %= Mod; NTT(A, -1); } } T; int n; char str[N]; ll Zero[N], One[N]; bitset<N> Ban; int main () { File(); scanf ("%s", str); n = strlen(str); For (i, 0, n - 1) if (str[i] == '0') Zero[i] = rand(); else if (str[i] == '1') One[i] = rand(); reverse(One, One + n); T.Mult(Zero, One, n, n); Ban.reset(); For (i, 0, 2 * n) if (Zero[i]) Ban[fabs((n - 1) - i)] = true; ll ans = 0; For (i, 1, n) { bool Pass = true; for (int j = i; j <= n; j += i) if (Ban[j]) { Pass = false; break; } if (Pass) ans ^= (1ll * (n - i) * (n - i)); } ans ^= (1ll * n * n); printf ("%lld\n", ans); return 0; }

__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/9146480.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(797)  评论(4编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示