ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

省选测试10

2021-03-11 07:01:24  阅读:174  来源: 互联网

标签:10 frac 省选 ++ mid long int 测试 calc


省选测试 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(n\log 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] << "\n";
    }
}

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] << "!!!\n";
        return h1[r] - h1[l - 1] * pow_B[r - l + 1];
    }
    if(f == 2) {
        // cout << l << " " << r << " " << ch2[l] << "!!!!\n";
        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 << "\n";
            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 << "\n";
    }
}

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) << "\n";
    Hash();
    Manacher(1); Work();
    Manacher(2); Work();
    // cout << (ch1 + 1) << "\n";
    // for(int i = 1;i <= n; i++) cout << i << ":" << r_[i * 2 + 1] << "\n";
    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}^d\dots 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_ << "\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 << "---------->\n";
    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 << "!!!!!!\n";
    }
    // cout << CRT() << "\n";
    return CRT();
}

void dfs(int now, int number) {
    // cout << now << "!!!\n";
    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] << "!!!!!!-----\n";
    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 << "+++\n";
        printf("%d\n", ksm(m, ANS, mod));
    }

    fclose(stdin); fclose(stdout);

    return 0;
}

/*
2
4 1
4 2

1
999999 99999999
*/

标签:10,frac,省选,++,mid,long,int,测试,calc
来源: https://www.cnblogs.com/czhui666/p/14515444.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有