• 省选测试10


    省选测试 10

    T1

    ​ 原题不硕了.

    T2

    给一个长度为(n)的字符串(S),有(n-2)次修改操作,第(i)次操作会将第(i+1)个字符变成(w_i)

    你需要在每次操作之后(包括未操作时)输出这个字符串的最长回文子串,即修改对后续有影响。

    对于你来说,只需要输出一个(ans),表示每次操作之后的答案的最大值就可以了。

    (n <= 1e5)

    ​ Manacher + 二分 + Hash.

    ​ 转化后的题面 : 给定一个字符串(A)和一个字符串(B).然后选出最长的是回文串的(A_i-A_j+B_{j+1}-B_k)这一段字符串.

    ​ 我们可以知道 : 符合条件的字符串一定是中间一段是回文串, 左右两边是长度相等的反串.对于中间那个回文串, 我们跑一边Manacher就可以得到了, 然后两边的直接用二分加Hash去判断.

    ​ 这样并不会枚举到所有的合法回文串, 但是我们贪心的选取了中间一段最长的, 然后再向两边扩展, 就可以得到最大的答案了.

    (O(nlog n))

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 5e5;
    int n, ans;
    int r_[N];
    char s[N], ch1[N], ch2[N];
    
    void Manacher(int f) {
        int len = 0, mx = 0, p = 0;
        s[++ len] = '$';
        if(f == 1) for(int i = 1;i <= n; i++) s[i * 2] = '#', s[i * 2 + 1] = ch1[i];
        if(f == 2) for(int i = 1;i <= n; i++) s[i * 2] = '#', s[i * 2 + 1] = ch2[i];
        len = n * 2 + 1; s[++ len] = '#'; s[++ len] = '@';
        for(int i = 1;i <= len; i++) {
            if(i > mx) r_[i] = 1;
            else r_[i] = min(r_[2 * p - i], mx - i + 1);
            while(s[i + r_[i]] == s[i - r_[i]]) r_[i] ++;
            if(i + r_[i] - 1 > mx) mx = i + r_[i] - 1, p = i;
            ans = max(ans, r_[i] - 1);
            // if(s[i] != '$' && s[i] != '@') cout << i << " " << s[i] << " " << r_[i] << "
    ";
        }
    }
    
    const int B = 233, mod = 998244353;
    unsigned long long h1[N], h2[N], pow_B[N];
    
    void Hash() {
        for(int i = 1;i <= n; i++) h1[i] = h1[i - 1] * B + ch1[i];
        reverse(ch2 + 1, ch2 + n + 1);
        for(int i = 1;i <= n; i++) h2[i] = h2[i - 1] * B + ch2[i];
        reverse(ch2 + 1, ch2 + n + 1);
    }
    
    unsigned long long get_hash(int f, int l, int r) {
        if(f == 1) {
            // cout << l << " " << r << " " << ch1[l] << "!!!
    ";
            return h1[r] - h1[l - 1] * pow_B[r - l + 1];
        }
        if(f == 2) {
            // cout << l << " " << r << " " << ch2[l] << "!!!!
    ";
            l = n - l + 1, r = n - r + 1; 
            return h2[l] - h2[r - 1] * pow_B[l - r + 1];
        }
    }
    
    void Work() {
        for(int i = 1;i <= n; i++) {
            int len = r_[i * 2 + 1] - 1;
            int L = i - len / 2 - 1, R = i + len / 2 + 1;
            int l = len / 2 + 1, r = min(i - 1, n - i), mid, res = 0;
            while(l <= r) { 
                mid = (l + r) >> 1;
                // if(i == 4) cout << l << " " << r << "
    ";
                if(get_hash(2, i - mid, L) == get_hash(1, R, i + mid)) res = mid, l = mid + 1;
                else r = mid - 1;
            }
            ans = max(ans, res * 2 + 1);
            
            len = r_[i * 2] - 1;
            L = i - len / 2 + 1, R = i + len / 2;
            l = len / 2 + 1, r = min(i, n - i), mid, res = 0;
            while(l <= r) {
                mid = (l + r) >> 1;
                if(get_hash(2, i - mid + 1, L) == get_hash(1, R, i + mid)) res = mid, l = mid + 1;
                else r = mid - 1;
            }
            ans = max(ans, res * 2);
            // cout << i << ":" << ans << "
    ";
        }
    }
    
    int main() {
    
        // freopen("str.in","r",stdin); freopen("str.out","w",stdout);
    
        scanf("%d", &n);
        pow_B[0] = 1;
        for(int i = 1;i <= n; i++) pow_B[i] = pow_B[i - 1] * B;
        cin >> (ch1 + 1);
        ch2[1] = ch1[1]; ch2[n] = ch1[n];
        for(int i = 2;i <= n - 1; i++) cin >> ch2[i];
        // cout << (ch2 + 1) << "
    ";
        Hash();
        Manacher(1); Work();
        Manacher(2); Work();
        // cout << (ch1 + 1) << "
    ";
        // for(int i = 1;i <= n; i++) cout << i << ":" << r_[i * 2 + 1] << "
    ";
        printf("%d", ans);
    
        fclose(stdin); fclose(stdout);
    
        return 0;
    }
    
    /*
    6
    ABAECB
    B
    C
    D
    E
    
    10
    AAAAAAAAAA
    A
    B
    C
    D
    E
    F
    G
    H
    */
    

    T3

    称一个的无向图是好的,满足:

    • 任意一个子连通图的点数都相等,且都为完全图。

    我们将所有(n)个点的好无向图拿出来,产生一个集合,每个好无向图是一个元素。

    现在有(m)种颜色,求染色方案数,模(999999599)

    两种方案数不同当且仅当存在一个元素的颜色不同。

    (n <= 1e9, m <= 1e9)

    ​ 如果说这个集合的大小为(k), 那么最后答案就是(k^m), 直接快速幂就好了.

    ​ 主要是怎么求(k), 首先我们可以列出这个式子:

    [k = displaystyle sum_{d mid n} frac{C_{n}^d*C_{n-d}^ddots C_{d}^{d}}{frac{n}{d}!} ]

    ​ 意思就就是从把这(n)个点分成(frac{n}{d})份, 每份选(d)个的不同方案数.

    ​ 化简一下 :

    [k = displaystyle sum_{d mid n} frac{n!}{d!^{frac{n}{d}}frac{n}{d}!} ]

    ​ 然后求就好了, 注意到(n <= 1e9), 所以它的约数要(dfs)搜出来.

    ​ 还注意到(P-1)不是质数, 所以我们还需要用CRT做. 注意算阶乘模一个数P的时候, 需要像扩展Lucas一样, 否则分母就会出现0.

    #include <bits/stdc++.h>
    
    #define int long long
    
    using namespace std;
    
    inline long long read() {
        long long s = 0, f = 1; char ch;
        while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
        for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
        return s * f;
    }
    
    const int N = 1e6, mod = 999999599;
    int n, m, cnt;
    int prime[N], is_prime[N];
    
    int ksm(int x, int y, int modp) {
        int res = 1;
        while(y) { if(y & 1) res = 1ll * res * x % modp; x = 1ll * x * x % modp; y >>= 1; }
        return res;
    }
    
    int inv(int x, int modp) {
        return ksm(x, modp - 2, modp);
    }
    
    void make_prime() {
        for(int i = 2;i < N; i++) {
            if(!is_prime[i]) prime[++ cnt] = i;
            for(int j = 1;j <= cnt && i * prime[j] < N; j++) {
                is_prime[i * prime[j]] = 1;
                if(!(i % prime[j])) break ;
            }
        } 
    }
    
    const int mod_1 = 999999598;
    int tot, ANS;
    int p[N], c[N], a[6], M[6], t[6], P[6] = {2, 13, 5281, 7283};
    
    int calc_t(int x, int p_) {
        int res = 0, base = p_;
        while(x >= p_) res += x / p_, p_ *= base;
        return res;
    }
    
    int calc_q(int n_, int modp) {
        if(n_ <= 1) return 1;
        // cout << n_ << "
    ";
        int res = calc_q(n_ / modp, modp), H = 1;
        for(int i = 1;i < modp; i++) H = 1ll * H * i % modp; 
        H = ksm(H, n_ / modp, modp);
        res = 1ll * res * H % mod;
        for(int i = n_ / modp * modp + 1;i <= n_; i++) res = 1ll * res * i % modp;
        return res; 
    }
    
    int CRT() {
        int res = 0;
        for(int i = 0;i < 4; i++) M[i] = mod_1 / P[i], t[i] = ksm(M[i], P[i] - 2, P[i]);
        for(int i = 0;i < 4; i++) res = (res + 1ll * a[i] * M[i] % mod_1 * t[i] % mod_1) % mod_1;
        return res;
    }
    
    int calc(int d) {
        // cout << d << "---------->
    ";
        int n_d = n / d;
        for(int i = 0;i < 4; i++) {
            int A = calc_t(n, P[i]), B = calc_t(d, P[i]), C = calc_t(n_d, P[i]);
            if(A - C - B * n_d > 0) a[i] = 0;
            else a[i] = 1ll * calc_q(n, P[i]) * inv(ksm(calc_q(d, P[i]), n_d, P[i]), P[i]) % P[i] * inv(calc_q(n_d, P[i]), P[i]) % P[i]; 
            // cout << i << "!!!!!!
    ";
        }
        // cout << CRT() << "
    ";
        return CRT();
    }
    
    void dfs(int now, int number) {
        // cout << now << "!!!
    ";
        if(now == tot + 1) { ANS += calc(number); return ; }
        int res = 1;
        for(int i = 0;i <= c[now]; i++) {
            dfs(now + 1, number * res); res *= p[now];
        }
    }
    
    void div(int n_) {
        tot = 0;
        memset(c, 0, sizeof(c));
        int tmp = n_;
        for(int i = 1;i <= cnt && prime[i] <= tmp; i++) 
            if(!(tmp % prime[i])) {
                p[++ tot] = prime[i];
                while(!(tmp % prime[i])) c[tot] ++, tmp /= prime[i];
            }
        if(tmp > 1) p[++ tot] = tmp, c[tot] = 1;
        // for(int i = 1;i <= tot; i++) cout << p[i] << " " << c[i] << "!!!!!!-----
    ";
        dfs(1, 1);
    }
    
    signed main() {
    
        freopen("count.in","r",stdin); freopen("count.out","w",stdout);
    
        make_prime();
        for(int T = read(); T ; T --) {
            n = read(); m = read(); ANS = 0;
            div(n); 
            // cout << ANS << "+++
    ";
            printf("%d
    ", ksm(m, ANS, mod));
        }
    
        fclose(stdin); fclose(stdout);
    
        return 0;
    }
    
    /*
    2
    4 1
    4 2
    
    1
    999999 99999999
    */
    
  • 相关阅读:
    Oracle中TO_DATE格式
    实现带查询功能的Combox控件
    Combox和DropDownList控件的区别
    C# 获取字符串中的数字
    C# try catch finally 执行
    树形DP codevs 1814 最长链
    codevs 2822 爱在心中
    匈牙利算法 cojs.tk 搭配飞行员
    匈牙利算法 codevs 2776 寻找代表元
    2016-6-19 动态规划,贪心算法练习
  • 原文地址:https://www.cnblogs.com/czhui666/p/14515444.html
Copyright © 2020-2023  润新知