CCPC2021 威海 810975 和 CCPC2021 网络赛(重赛) Subpermutation

810975

How many situations are there if I play \(n\) games and win \(m\) games, and the longest winning streak is \(k\)?

If we use 1 to represent victory and 0 to represent defeat, any string consisting of 0 and 1 of length \(n\) can represent a situation of an \(n\)-round game. Two situations are different if and only if the two 01 strings are different.

\(0≤n,m,k≤10^5\)

题解

要求最长的1连续段长度为\(k\),并不好直接做。

考虑容斥,分析容易求的是什么。容易发现如果钦定某长为\(k\)的1连续段后剩下的随便选,那么最长1连续段长度一定是大于等于\(k\)的。

所以我们用最长1连续段长度大于等于\(k\)的方案数减去最长1连续段长度大于等于\(k+1\)的方案数即可。

现在关键在于这个最长1连续段长度大于等于\(k\)的方案数怎么求。

如果使用“钦定第一个长为\(k\)的1连续段的位置,之前的位置不能出现长度大于等于\(k\)的1连续段,之后的位置任选”的方式,并不能做。因为这个“之前的位置不能出现长度大于等于\(k\)的1连续段”的方案数不能直接算。

考虑“之前的位置也任选”,当然会算重。那么进行第二步容斥就好了。

\[\text{ans}_k=\sum_{i=1}^{\lfloor m/k\rfloor}(-1)^{i-1}\binom{n-m+1}{i}\binom{n-i*k}{m-i*k} \]

输出\(\text{ans}_k-\text{ans}_{k+1}\)即可。

时间复杂度\(O(n)\)。其实这题可以直接询问\(k\)\(1\)\(m\)的答案的。

#include<bits/stdc++.h>
using namespace std;
 
typedef long long ll;
const ll mod = 998244353;
const int maxn = 100005;
 
vector < ll > fac, inv, finv;
 
void init() {
    fac.resize(maxn);
    finv.resize(maxn);
    inv.resize(maxn);
    fac[0] = fac[1] = 1;
    inv[1] = 1;
    finv[0] = finv[1] = 1;
    for (int i = 2; i < maxn; i++) {
        fac[i] = fac[i - 1] * i % mod;
        inv[i] = mod - mod / i * inv[mod % i] % mod;
        finv[i] = finv[i - 1] * inv[i] % mod;
    }
}
ll binom(ll n, ll r) {
    if (n < r || n < 0 || r < 0) return 0;
    return fac[n] * finv[r] % mod * finv[n - r] % mod;
}
 
int main() {
    init();
    int n, m, k; cin >> n >> m >> k;
    if (k == 0) {
        printf("%d\n", m == 0);
        return 0;
    }
    ll ans1 = 0, ans2 = 0;
    for (ll i = 1; i * k <= m; i++) {
        if (i & 1) ans1 = (ans1 + binom(n - m + 1, i) % mod * binom(n - i * k, n - m) % mod + mod) % mod;
        else ans1 = (ans1 - binom(n - m + 1, i) % mod * binom(n - i * k, n - m) % mod + mod) % mod;
    }
    for (ll i = 1; i * (k + 1) <= m; i++) {
        if (i & 1) ans2 = (ans2 + binom(n - m + 1, i) % mod * binom(n - i * (k + 1), n - m) % mod + mod) % mod;
        else ans2 = (ans2 - binom(n - m + 1, i) % mod * binom(n - i * (k + 1), n - m) % mod + mod) % mod;
    }
    cout << (ans1 - ans2 + mod) % mod << endl;
    return 0;
}

Subpermutation

A permutation of n is a sequence of length n in which each number from 1 to n appears exactly once. A full permutation of \(n\) is a sequence that connects all permutations of n into one sequence in lexicographical order. Sequence \(p_1, p_2, . . . , p_n\) is lexicographically smaller than \(q_1, q_2, . . . , q_n\) if \(p_i < q_i\) where \(i\) is the minimum index satisfying \(p_i \neq q_i\)

Here are some symbols used in this problem:

Symbol Definition Example
$p_n$ the full-permutation of $n$ $p_3=\{1, 2, 3, 1, 3, 2, 2, 1, 3, 2, 3, 1, 3, 1, 2, 3, 2, 1\}$
$S_n$ the set of all permutations of $n$ $S_3=\{\{1,2,3\},\{1,3,2\},\{2,1,3\},\{2,3,1\},\{3,1,2\},\{3,2,1\}\}$
$f(s,t)$ the number of contiguous subsequences in $s$ that are equal to $t$ $f(\{1,2, 12, 1, 2\}, \{1, 2\})=2$

Now given \(n\) and \(m\), please calculate

\[\sum_{t\in S_m} f(p_n, t) \mod 10^9+7 \]

\(1\leq T\leq 10^5, 1\leq n\leq 10^6, 1\leq m\leq n\)

题解

\(m<n\)

考虑某一个特定的\(t\)\(f(p_n, t)\)

首先是\(t\)在大排列内部的情况,显然方案数为\((n-m+1)!\)

然后是\(t\)分成两部分\(t_1, t_2\),在前后两个大排列的连接处。\(t_1\)是前一个大排列的后缀,\(t_2\)是后一个大排列的前缀。

如果前一个大排列的前缀也是\(t_2\),那么前一个大排列的中间部分任意排,结合\(t_1, t_2\)的划分数,方案数为\((m-1)(n-m)!\)。有一个特殊情况,若\(t_1\)是从大到小的话,那么前一个大排列的中间部分就不能也是从大到小。因为\(t_1, t_2\)已经包含了\(1\sim m\),中间的部分必然是\(n+1\sim m\),如果中间部分和\(t_1\)都是从大到小排的话,后一个大排列的前缀就会发生变化而不再是\(t_2\)

如果前一个大排列的前缀不是\(t_2\),也就是某个位置不同。考虑前一个大排列在这个位置之后的部分,它必须是从大到小排列的。由于\(t_2\)中的数一定小于中间的部分的数,所以这种情况是不存在的。

统计所有的\(t\),总答案为

\[\text{ans}=m!((n-m+1)!+(m-1)(n-m)!)-\sum_{i=1}^{m-1}\binom{m}{i}(m-i)! \]

\(m=n\)

唯一一个需要考虑的情况就是“\(t\)分成两部分\(t_1, t_2\),在前后两个大排列的连接处,而前一个大排列的前缀不是\(t_2\)”的情况。

那么考虑前一个大排列的前缀与\(t_2\)不同的位置之后,一定是从大到小排列的。根据字典序,后一个大排列的后缀是从小到大排列的,那么一定有\(t_2\)\(t_1\)中的数交换位置,也就形成矛盾。

代码和\(m<n\)的情况是一样的,并不需要特判。

时间复杂度\(O(n+T)\)

constexpr int N=1e6+10;
int fac[N]={1, 1}, inv[N]={-1, 1}, ifac[N]={1, 1}, sum[N]={0, 1};
 
void realMain(){
	int n=read<int>(), m=read<int>();
	int ans=add(fac[n-m+1], mul(m-1, fac[n-m]));
	ans=mul(ans, fac[m]);
	ans=add(ans, MOD-mul(fac[m], sum[m-1]));
	printf("%d\n", ans);
}
int main(){
	int n=1e6;
	for(int i=2; i<=n; ++i){
		fac[i]=mul(fac[i-1], i);
		inv[i]=mul(MOD-MOD/i, inv[MOD%i]);
		ifac[i]=mul(ifac[i-1], inv[i]);
		sum[i]=add(sum[i-1], ifac[i]);
	}
	for(int t=read<int>(); t--; ) realMain();
	return 0;
}

posted on 2022-09-05 11:41  autoint  阅读(89)  评论(0编辑  收藏  举报

导航