Educational Codeforces Round 113 C. Jury Meeting (思维+组合数学)

12 篇文章 0 订阅

https://codeforces.com/contest/1569/problem/C

题意:

n个数,每经过一个数它就-1,如果当所有数减完的时候没有一个数连续减了两次那这样一个序列就叫好序列,求所有好序列的数量,答案mod998244353

题解:

我们可以发现不管n是多少,到最后我们都只用看只剩下最后两个数的情况,而剩下的两个数肯定是最大数和次大数.

所以我们可以分以下三种情况讨论:

1.最大数=次大数

和位置无关,所有排列的序列都是好序列,答案为n!

2.最大数>=次大数+2

所有序列都不是好序列,答案为0

3.最大数只有一个,且次大数和最大数的差值为1

我们通过观察可以发现,至少有一个次大数要在最大数的右边才是好序列,那么答案可以为全排列减去次大数全都在最大数左边的情况.

次大数圈都在最大数左边的方案数可以通过枚举最大数的位置来计算:

我们设有cnt个次大数,那么最大数就可以放在n~cnt+1的位置上.设最大数的位置为i,最大数左边的空位就是i-1个,右边的空位就是n- i个,次大数只能放在左边的位置,所以有 ( c n t i − 1 ) \binom{cnt}{i-1} (i1cnt)种放法,现在已经放了cnt个次大数和一个最大数,所以还剩下n-cnt-1个数,剩下的数可以随意放所以就是 ( n − c n t − 1 ) ! (n-cnt-1)! (ncnt1)!放法.

( n − c n t − 1 ) ! ∗ ( c n t i − 1 ) (n-cnt-1)!*\binom{cnt}{i-1} (ncnt1)!(i1cnt)就是最大数放在第i个位置时不合法的方案数,枚举i从n~cnt+1,依次减掉即可.

代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<unordered_map>
#include<set>

#pragma GCC optimize(2)
using namespace std;
#define ll long long
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define fi first
#define se second
#define pb push_back
#define debug(a) cout << #a << " " << a << '\n';
const int N = 2e5 + 5;
const int M = 1e5 + 5;
const int INF = 0x3f3f3f3f;
const ll LLINF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 998244353;

inline ll read();

ll qpow(ll a, ll b) {
    ll res = 1;
    while (b) {
        if (b & 1)res = res * a % mod;
        a = (a * a) % mod;
        b >>= 1;
    }
    return res;
}

int n, m, t;
ll a[N];
ll pre[N];//pre[i]=1*2*...*i
ll A(ll nn, ll mm) {
    return pre[nn] * qpow(pre[nn - mm], mod - 2) % mod;
}

void solve() {
    cin >> n;
    ll maxx = -1;

    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    sort(a, a + n);
    if (a[n - 1] >= a[n - 2] + 2)cout << 0 << '\n';
    else if (a[n - 1] == a[n - 2])cout << pre[n] << '\n';
    else {
        int cnt = 0;
        for (int i = 0; i < n; i++) {
            if (a[i] == a[n - 2])cnt++;//最大值不能在所有次大值的最右边
        }
        ll ans = pre[n];
        for (int i = n; i >= cnt + 1; i--) { //i是最大值的位置 枚举i所在位置不是好序列的方案数
            ll res = (A(i - 1, cnt) * pre[n - cnt - 1]) % mod;
            //cout<<res<<'\n';
            ans = (ans - res + mod) % mod;
        }
        cout << ans % mod << '\n';

    }

}

int main() {
    //ios::sync_with_stdio(false);
    t = read();
    pre[0] = 1;

    for (int i = 1; i <= 2e5; i++) {
        pre[i] = (pre[i - 1] * i) % mod;
    }
    while (t--) {
        solve();
    }

    return 0;
}


inline ll read() {
    char ch = getchar();
    ll p = 1, data = 0;
    while (ch < '0' || ch > '9') {
        if (ch == '-')p = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        data = data * 10 + (ch ^ 48);
        ch = getchar();
    }
    return p * data;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
"educational codeforces round 103 (rated for div. 2)"是一个Codeforces平台上的教育性比赛,专为2级选手设计评级。以下是有关该比赛的回答。 "educational codeforces round 103 (rated for div. 2)"是一场Codeforces平台上的教育性比赛。Codeforces是一个为程序员提供竞赛和评级的在线平台。这场比赛是专为2级选手设计的,这意味着它适合那些在算法和数据结构方面已经积累了一定经验的选手参与。 与其他Codeforces比赛一样,这场比赛将由多个问题组成,选手需要根据给定的问题描述和测试用例,编写程序来解决这些问题。比赛的时限通常有两到三个小时,选手需要在规定的时间内提交他们的解答。他们的程序将在Codeforces的在线评测系统上运行,并根据程序的正确性和效率进行评分。 该比赛被称为"educational",意味着比赛的目的是教育性的,而不是针对专业的竞争性。这种教育性比赛为选手提供了一个学习和提高他们编程技能的机会。即使选手没有在比赛中获得很高的排名,他们也可以从其他选手的解决方案中学习,并通过参与讨论获得更多的知识。 参加"educational codeforces round 103 (rated for div. 2)"对于2级选手来说是很有意义的。他们可以通过解决难度适中的问题来测试和巩固他们的算法和编程技巧。另外,这种比赛对于提高解决问题能力,锻炼思维和提高团队合作能力也是非常有帮助的。 总的来说,"educational codeforces round 103 (rated for div. 2)"是一场为2级选手设计的教育性比赛,旨在提高他们的编程技能和算法能力。参与这样的比赛可以为选手提供学习和进步的机会,同时也促进了编程社区的交流与合作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值