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


    题面

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

    题解

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

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

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

    后来发现太年轻了 qwq

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

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

    这个容易理解 :

    如图所示 img

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

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

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

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

    那么对于任意的 (y | x)(y) 都是不可行的 , 这就意味着 (border)(n - y) 的时候也不可行 .

    $displaystyle ecause x mod y = 0 $ 且 (i equiv j pmod x)

    (displaystyle herefore 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
    ", ans);
    
    	return 0;
    }
    
  • 相关阅读:
    JS判断单选框是否选中
    Js判断是否有属性
    判断是否有焦点
    Js 替代
    Js解析json
    回车事件
    js解析XML
    Linux常用基础(三)
    Linux常用基础(二)
    Linux常用基础(一)
  • 原文地址:https://www.cnblogs.com/zjp-shadow/p/9146480.html
Copyright © 2020-2023  润新知