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;
}
### Codeforces Educational Round 26 比赛详情 Codeforces是一个面向全球程序员的比赛平台,其中Educational Rounds旨在帮助参与者提高算法技能并学习新技巧。对于具体的Educational Round 26而言,这类比赛通常具有如下特点: - **时间限制**:每道题目的解答需在规定时间内完成,一般为1秒。 - **内存限制**:程序运行所占用的最大内存量被限定,通常是256兆字节。 - 输入输出方式标准化,即通过标准输入读取数据并通过标准输出打印结果。 然而,关于Educational Round 26的具体题目细节并未直接提及于提供的参考资料中。为了提供更精确的信息,下面基于以往的教育轮次给出一些常见的题目类型及其解决方案思路[^1]。 ### 题目示例与解析 虽然无法确切描述Educational Round 26中的具体问题,但可以根据过往的经验推测可能涉及的问题类别以及解决这些问题的一般方法论。 #### 类型一:贪心策略的应用 考虑一个问题场景,在该场景下需要照亮一系列连续排列的对象。假设存在若干光源能够覆盖一定范围内的对象,则可以通过遍历整个序列,并利用贪心的思想决定何时放置新的光源以确保所有目标都被有效照射到。这种情况下,重要的是保持追踪当前最远可到达位置,并据此做出决策。 ```cpp #include <bits/stdc++.h> using namespace std; bool solve(vector<int>& a) { int maxReach = 0; for (size_t i = 0; i < a.size(); ++i) { if (maxReach < i && !a[i]) return false; if (a[i]) maxReach = max(maxReach, static_cast<int>(i) + a[i]); } return true; } ``` #### 类型二:栈结构处理匹配关系 另一个常见问题是涉及到成对出现元素之间的关联性判断,比如括号表达式的合法性验证。这里可以采用`<int>`类型的栈来记录左括号的位置索引;每当遇到右括号时就弹出最近一次压入栈底的那个数值作为配对依据,进而计算两者间的跨度长度累加至总数之中[^2]。 ```cpp #include <stack> long long calculateParens(const string& s) { stack<long long> positions; long long num = 0; for(long long i = 0 ; i<s.length() ;++i){ char c=s[i]; if(c=='('){ positions.push(i); }else{ if(!positions.empty()){ auto pos=positions.top(); positions.pop(); num+=i-pos; } } } return num; } ``` #### 类型三:特定模式下的枚举法 针对某些特殊条件约束下的计数类问题,如寻找符合条件的三位整数的数量。此时可通过列举所有可能性的方式逐一检验是否符合给定规则,从而统计满足要求的结果数目。例如求解形如\(abc\)形式且不含重复数字的正整数集合大小[^3]。 ```cpp vector<int> generateSpecialNumbers(int n) { vector<int> result; for (int i = 1; i <= min(n / 100, 9); ++i) for (int j = 0; j <= min((n - 100 * i) / 10, 9); ++j) for (int k = 0; k <= min(n % 10, 9); ++k) if ((100*i + 10*j + k)<=n&&!(i==0||j==0)) result.emplace_back(100*i+10*j+k); sort(begin(result), end(result)); return result; } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值