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

題面

LOJ #6436. 「PKUSC2018」神仙的遊戲php

題解

參考 yyb 的口中的長郡最強選手 租酥雨大佬的博客 ...html

一開始覺得 通配符匹配 就是相似於 BZOJ 4259: 殘缺的字符串 這樣作 .c++

把通配符設成 \(0\) 而後 . 別的按 \(\mathrm{ASCII}\) 碼 給值 , 最後把他寫成式子的形式 ...git

後來發現太年輕了 qwq優化

先要作這題 , 那麼先發現性質咯 :ui

存在一個長度爲 \(len\)\(border\) 當且僅當對於 \(\forall i∈[1,n−len]\)\(s[i]=s[n−len+i]\)
或者這樣說,把全部位置在模 \(n−len\) 意義下分組,同一組裏的 \(01\) 要所有相同。spa

這個容易理解 :3d

如圖所示 imgcode

上下 棕色 分別爲先後綴能夠匹配上的部分 , 紅色箭頭 表示向後移動 \(n - len\) 個 長度 .orm

能夠把 棕色 抽象成向右平移了 \(n-len\) 個位置 , 相對位置上的數仍然不變 .

有了這個性質 , 咱們能夠對於答案進行判斷了 .

也就是對於每一對 \(01\) , 假設 \(0\) 所處位置爲 \(i\) , \(1\) 位置爲 \(j\) . \(x = |i - j|\)

那麼對於任意的 \(y | x\)\(y\) 都是不可行的 , 這就意味着 \(border\)\(n - y\) 的時候也不可行 .

$\displaystyle \because x \bmod y = 0 $ 且 \(i \equiv j \pmod x\)

\(\displaystyle \therefore i \equiv j \pmod y\) , 因此沒法分到同一組 .

那麼咱們只要求出全部可能的 \(x\) 而後用 \(O(n \ln n)\) 枚舉倍數判斷一個數 , 就能夠了 .

暴力枚舉 \(0,1\) 對能夠拿滿暴力分 ....

正解的話只要優化枚舉的複雜度 就好了 .

不難發現原式是 \(x=|i - j|\) 咱們能夠構造卷積

\(\displaystyle A = \sum_{i=0}^{n} [str_{i}=0] x^i,B=\sum_{i=0}^{n}[str_{n-i}=1]x^i , A*B=C\) .

按前面的假設 , 如有 \(0\)\(i\) , \(1\)\(j\) . 那麼 \(|i - j|\) 必是不合法的 .

\(C\) 中咱們發現 \(i-j\) 對應的是 \(n + (i - j)\) , 那麼就能夠直接作完了 .

NTT 優化卷積就好了 . 總時間複雜度 \(O(n \log n)\) .

總結

能夠利用 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;
}
相關文章
相關標籤/搜索