CCPC2021 威海 810975 和 CCPC2021 网络赛(重赛) Subpermutation
810975
How many situations are there if I play games and win games, and the longest winning streak is ?
If we use 1 to represent victory and 0 to represent defeat, any string consisting of 0 and 1 of length can represent a situation of an -round game. Two situations are different if and only if the two 01 strings are different.
题解
要求最长的1连续段长度为,并不好直接做。
考虑容斥,分析容易求的是什么。容易发现如果钦定某长为的1连续段后剩下的随便选,那么最长1连续段长度一定是大于等于的。
所以我们用最长1连续段长度大于等于的方案数减去最长1连续段长度大于等于的方案数即可。
现在关键在于这个最长1连续段长度大于等于的方案数怎么求。
如果使用“钦定第一个长为的1连续段的位置,之前的位置不能出现长度大于等于的1连续段,之后的位置任选”的方式,并不能做。因为这个“之前的位置不能出现长度大于等于的1连续段”的方案数不能直接算。
考虑“之前的位置也任选”,当然会算重。那么进行第二步容斥就好了。
输出即可。
时间复杂度。其实这题可以直接询问从到的答案的。
#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 is a sequence that connects all permutations of n into one sequence in lexicographical order. Sequence is lexicographically smaller than if where is the minimum index satisfying
Here are some symbols used in this problem:
Symbol | Definition | Example |
---|---|---|
the full-permutation of | ||
the set of all permutations of | ||
the number of contiguous subsequences in that are equal to |
Now given and , please calculate
题解
当时
考虑某一个特定的的。
首先是在大排列内部的情况,显然方案数为。
然后是分成两部分,在前后两个大排列的连接处。是前一个大排列的后缀,是后一个大排列的前缀。
如果前一个大排列的前缀也是,那么前一个大排列的中间部分任意排,结合的划分数,方案数为。有一个特殊情况,若是从大到小的话,那么前一个大排列的中间部分就不能也是从大到小。因为已经包含了,中间的部分必然是,如果中间部分和都是从大到小排的话,后一个大排列的前缀就会发生变化而不再是。
如果前一个大排列的前缀不是,也就是某个位置不同。考虑前一个大排列在这个位置之后的部分,它必须是从大到小排列的。由于中的数一定小于中间的部分的数,所以这种情况是不存在的。
统计所有的,总答案为
当时
唯一一个需要考虑的情况就是“分成两部分,在前后两个大排列的连接处,而前一个大排列的前缀不是”的情况。
那么考虑前一个大排列的前缀与不同的位置之后,一定是从大到小排列的。根据字典序,后一个大排列的后缀是从小到大排列的,那么一定有和中的数交换位置,也就形成矛盾。
代码和的情况是一样的,并不需要特判。
时间复杂度。
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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
2019-09-05 test20190905 ChiTongZ
2018-09-05 [ZJOI2010]基站选址