Educational Codeforces Round 57 E,F,G

E. The Top Scorer

题意:输入p,s,r。
a 1 + a 2 + . . . + a p = s a 1 > = r , a i > = 0 a_1+a_2+...+a_p=s\\ a_1>=r,a_i>=0\\ a1+a2+...+ap=sa1>=r,ai>=0
a 1 a_1 a1如果是最大值,最大值有k个,则有 1 k \frac{1}{k} k1几率获胜,求获胜的概率。
解法:
x 1 + x 2 + . . . + x m = n x i > = 0 x_1+x_2+...+x_m=n\\ x_i>=0 x1+x2+...+xm=nxi>=0
( n + m − 1 m − 1 ) \binom{n+m-1}{m-1} (m1n+m1)个解,隔板法。
x 1 + x 2 + . . . + x m = n x i > = k x_1+x_2+...+x_m=n\\ x_i>=k x1+x2+...+xm=nxi>=k
( n − m ∗ k − 1 m − 1 ) \binom{n-m*k-1}{m-1} (m1nmk1)个解,这个只需要把上面式子换成 y 1 + y 1 + . . . + y m = n − m ∗ k . y i > = 0 y_1+y_1+...+y_m=n-m*k.y_i>=0 y1+y1+...+ym=nmk.yi>=0
x 1 + x 2 + . . . + x m = n x i &lt; = k x_1+x_2+...+x_m=n\\ x_i&lt;=k x1+x2+...+xm=nxi<=k
∑ i = 0 m ( − 1 ) i ( m i ) ( n − i ∗ ( k + 1 ) + m − 1 m − 1 ) \sum_{i=0}^{m}(-1)^i\binom{m}{i}\binom{n-i*(k+1)+m-1}{m-1} i=0m(1)i(im)(m1ni(k+1)+m1)个解,对上式进行容斥。
对这道题只考虑 a 1 a_1 a1是最大值的时候,枚举 a 1 a_1 a1的值,枚举与 a 1 a_1 a1值相同的个数,其他值小于 a 1 a_1 a1。复杂度 O ( p ∗ p ∗ s ) O(p*p*s) O(pps)

#include <bits/stdc++.h>

using namespace std;
const int mod = 998244353;
const int N = 6000;
int fac[N], fav[N];
int a[N];

int qpow(int x, int y)
{
    int ans = 1;
    while (y)
    {
        if (y & 1)
            ans = 1ll * ans * x % mod;
        x = 1ll * x * x % mod;
        y /= 2;
    }
    return ans;
}

void init()
{
    fac[0] = 1;
    for (int i = 1; i < N; i++)
        fac[i] = 1ll * fac[i - 1] * i % mod;
    fav[N - 1] = qpow(fac[N - 1], mod - 2);
    for (int i = N - 2; i >= 0; i--)
        fav[i] = 1ll * fav[i + 1] * (i + 1) % mod;
}

int C(int n, int m)
{
    if (n == m)
        return 1;
    if (n < 0 || m < 0 || n < m)
        return 0;
    return 1ll * fac[n] * fav[m] % mod * fav[n - m] % mod;
}

int main()
{
    init();
    int p, s, r;
    scanf("%d%d%d", &p, &s, &r);
    int ans = 0;
    for (int i = r; i <= s; i++)
    {
        for (int j = 1; j <= p; j++)
        {
            if (j * i > s)
                break;
            int tmp = 0;
            int inv = 1ll * qpow(j, mod - 2) * C(p - 1, j - 1) % mod;
            for (int k = 0, d = 1; k <= p - j; k++, d *= -1)
            {
                tmp = (tmp + 1ll * d * C(p - j, k) * C(s - i * j + (p - j) - 1 - k * (i), p - j - 1) % mod) % mod;
                tmp = (tmp + mod) % mod;
            }
            ans = (ans + 1ll * tmp * inv % mod) % mod;
        }
    }
    ans = 1ll * ans * qpow(C(s - r + p - 1, p - 1), mod - 2) % mod;
    printf("%d\n", ans);
    return 0;
}

F. Inversion Expectation

题意:n的排列,其中有些值不确定,用-1表示,这个排列可能是满足条件的任意一个排列(等概率)。求逆序数的期望。
解法:确定数之间:如果序列中没有-1,就是求序列的逆序数。不确定数与确定数:对一个确定的值 a i a_i ai来说,会对答案有贡献当前面有大于 a i a_i ai的数出现,或者后面有小于 a i a_i ai的数出现,对于小于(大于) a i a_i ai的值一起计算。不确定数之间:假设一共m个-1,相当于点对 i , j i,j i,j等概率的一前一后,期望是 1 2 \frac{1}{2} 21,所以对答案的贡献是 ( n − 1 2 ) ∗ 1 2 \binom{n-1}{2}*\frac{1}{2} (2n1)21,复杂度 O ( n l o g n + n + 1 ) O(nlogn+n+1) O(nlogn+n+1)

#include <bits/stdc++.h>

using namespace std;
const int N = 2e5 + 5;
const int mod = 998244353;
int a[N], fac[N], fav[N], sum[N];
int dp1[N], dp2[N];

int lowbit(int x)
{
    return x & (-x);
}

void update(int x)
{
    while (x < N)
    {
        sum[x]++;
        x += lowbit(x);
    }
}

int query(int x)
{
    int ans = 0;
    while (x)
    {
        ans += sum[x];
        x -= lowbit(x);
    }
    return ans;
}

int qpow(int x, int y)
{
    int ans = 1;
    while (y)
    {
        if (y & 1)
            ans = 1ll * ans * x % mod;
        x = 1ll * x * x % mod;
        y /= 2;
    }
    return ans;
}

void init()
{
    fac[0] = 1;
    for (int i = 1; i < N; i++)
        fac[i] = 1ll * fac[i - 1] * i % mod;
    fav[N - 1] = qpow(fac[N - 1], mod - 2);
    for (int i = N - 2; i >= 0; i--)
    {
        fav[i] = 1ll * fav[i + 1] * (i + 1) % mod;
    }
}

int main()
{
    init();
    int n, num = 0;
    scanf("%d", &n);
    for(int i=1;i<=n;i++) dp2[i]=1;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        if (a[i] == -1)
        {
            dp1[i] = 1;
            num++;
        }
        else{
            dp2[a[i]]=0;
        }
    }
    for (int i = 1; i <= n; i++)
    {
        dp1[i] += dp1[i - 1];
        dp2[i] += dp2[i - 1];

    }
    int ans = 0;

    for (int i = n; i >= 1; i--)
    {
        if (a[i] != -1)
        {
            ans = (ans + query(a[i])) % mod;
            update(a[i]);
        }
    }
    if (num == 0)
    {
        printf("%d\n", ans);
        return 0;
    }

    int inv = 1ll * fac[num - 1] * fav[num] % mod;
    for (int i = 1; i <= n; i++)
    {
        if (a[i] != -1)
        {
            ans = (ans + 1ll * inv * dp1[i] % mod * (num - dp2[a[i]]) % mod) % mod;
            ans = (ans + 1ll * inv * (num - dp1[i]) % mod * dp2[a[i]] % mod) % mod;
        }
    }
    ans = (ans + 1ll * num * (num - 1) % mod * qpow(4, mod - 2) % mod) % mod;
    printf("%d\n", ans);
    return 0;
}

G. Lucky Tickets

题意:长度为n(偶数)的数,每一位只能是序列 a i a_i ai中的值,求前一半的和等于后一半的和的序列的个数。
解法:
求用 a i a_i ai形成的和为m的序列个数,可以用简单的dp,但是复杂度过高。
通过生成函数求的话,相当于求多项式
( x a 1 + x a 2 + . . . + a a m ) n (x^{a_1}+x^{a_2}+...+a^{a_m})^n (xa1+xa2+...+aam)n
用NTT进行多项式乘法,用快速幂减少乘法次数。

#include <bits/stdc++.h>

#define LL long long int
#define ls (x << 1)
#define rs (x << 1 | 1)
#define MID int mid=(l+r)>>1
using namespace std;

const int N = 2100000;
const int Mod = 998244353;
int n, m, L, R[N], a[N], d[20], ans[N];

inline int QPow(int d, int z)
{
    int ans = 1;
    for (; z; z >>= 1, d = 1ll * d * d % Mod)
        if (z & 1)
            ans = 1ll * ans * d % Mod;
    return ans;
}

inline void NTT(int *A, int f)
{
    for (int i = 0; i < n; ++i)
        if (i < R[i])
            swap(A[i], A[R[i]]);
    for (int i = 1; i < n; i <<= 1)
    {
        int gn = QPow(3, (Mod - 1) / (i << 1)), x, y;
        for (int j = 0; j < n; j += (i << 1))
        {
            int g = 1;
            for (int k = 0; k < i; ++k, g = 1ll * g * gn % Mod)
            {
                x = A[j + k];
                y = 1ll * g * A[i + j + k] % Mod;
                A[j + k] = (x + y) % Mod;
                A[i + j + k] = (x - y + Mod) % Mod;
            }
        }
    }
    if (f == 1)
        return;
    reverse(A + 1, A + n);
    int y = QPow(n, Mod - 2);
    for (int i = 0; i < n; ++i)
        A[i] = 1ll * A[i] * y % Mod;
}

void qpow(int *x, int y)
{
    for (int i = 0; i < n; i++)
        ans[i] = 1;
    while (y)
    {
        if (y & 1)
        {
            for (int i = 0; i < n; i++)
                ans[i] = 1ll * ans[i] * x[i] % Mod;
        }
        for (int i = 0; i < n; i++)
        {
            x[i] = 1ll * x[i] * x[i] % Mod;
        }
        y /= 2;
    }
}

int main()
{
    n = 1000000;
    m = 1000000;

    m += n;
    for (n = 1; n <= m; n <<= 1)
        ++L;
    //printf("%d\n",n);
    for (int i = 0; i < n; ++i)
        R[i] = (R[i >> 1] >> 1) | ((i & 1) << (L - 1));

    int nn, kk;
    scanf("%d%d", &nn, &kk);

    for (int i = 1; i <= kk; i++)
    {
        scanf("%d", &d[i]);

        a[d[i]] = 1;
    }
    NTT(a, 1);

    qpow(a, nn / 2);

    NTT(ans, -1);
    int ANS = 0;
    for (int i = 0; i < n; i++)
    {
        ANS = (ANS + 1ll * ans[i] * ans[i] % Mod) % Mod;
    }

    printf("%d\n", ANS);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值