P7322 「PMOI-4」排列变换
2022/9/10 6:24:42
本文主要是介绍P7322 「PMOI-4」排列变换,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
P7322 「PMOI-4」排列变换
题目大意
给定常数 \(k\)。对于一个长度为 \(n\) 的排列 \(a\),定义
\[f(a)=\{\max_{1 \le i \le k} \{a_i\},\max_{2 \le i \le k+1} \{a_i\},\cdots,\max_{n-k+1 \le i \le n} \{a_i\}\} \]对于一个长度为 \(n\) 的序列 \(a\),定义其权值 \(w(a)\) 为 \(a\) 中不同的数的个数。
现在,\(\text{ducati}\) 想知道,对于所有长度为 \(n\) 的排列 \(p\),它们的 \(w(f(p))\) 之和。
分析
就是给定一个长度为 kk 的滑动窗口在长度为 n
的排列上滑,问滑动窗口中的 max
变化了多少次。(滑动窗口不懂的可以理解成序列中连续 k
个数。这里之所以不写不同数的个数而是变化次数,是因为题目求的是排列,而排列满足所有数各不相同,所以变化次数加 1
就等于不同 max
个数)
可以发现,因为枚举全排列中的每一个 i
都可以变成 n-i+1
(n!
个排列仍然完整),所以我们计算答案,求 min
和 max
其实是等价的。
因为向右滑动 1
长度的情况下,只有窗口最左的数会出窗口,而右边紧邻的那个数会进入窗口,所以我们实际上只需要对于每个窗口看最左边的数是否是窗口滑动之前的最小值,以及新加入的最右边的数是否是窗口滑动之后新的最小值即可。
对于最左边的数是窗口滑动之前是窗口最小值,进行一波推式子之后,可以发现它对答案的贡献有:
\[\sum_{i=1}^{n}C(n-i,k-1)(n-k)!(k-1)!(n-k) \]其中从左往右,求和符合是枚举这个最小值的大小,组合数是令窗口中剩余的 k-1
个数大于最小值的值的个数,两个阶乘分别是窗口外面的数任意排列和窗口内部除去最小值外任意排列,最后一个 n-k
是计算排列中窗口可以在的位置有 n-k
个。(其实总窗口有 n-k+1
个,就是最左边那个无法移动了)
然后对于新进入的最右边的数是新的窗口最小值,式子是可以类比的:
\[\sum_{i=1}^{n}C(n-i,k)(n-k-1)!k!(n-k) \]含义是类似的,只不过计算的是新加进去最右边那个数。
当然,只将这两部分加起来还有一些问题,实际上我们还会算重一部分,既满足最左边是窗口最小值,又满足新加入的是窗口的新最小值(且小于最左边的,或者你可以强制令最右边的是最小的,最左边的小于最右边的,实际上两种方法的式子是一样的),这部分答案需要减掉:
\[\sum_{i=1}^{n}C(n-i,k-1)(n-k-1)!(k-1)!(n-k)(i-1) \]前面同样是类似的,最后的 i-1
是最右边小于最左边的个数,或者你也可以理解成是最左边小于最右边的个数。
注意最后还需要加上每个排列都缺少的 1
答案,也就是总答案加上 n!
,因为我们最开始忽略了这部分。
Ac_code
#include<bits/stdc++.h> #define ios ios::sync_with_stdio(false); cin.tie(0), cout.tie(0) using namespace std; const int N = 5e5 + 10,mod = 998244353; template<int T> struct ModInt { const static int mod = T; int x; ModInt(int x = 0) : x(x % mod) {} int val() { return x; } ModInt operator + (const ModInt &a) const { int x0 = x + a.x; if (x0 >= mod) x0 -= mod; if (x0 < 0) x0 += mod; return ModInt(x0); } ModInt operator - (const ModInt &a) const { int x0 = x - a.x; if (x0 >= mod) x0 -= mod; if (x0 < 0) x0 += mod; return ModInt(x0); } ModInt operator * (const ModInt &a) const { return ModInt(1LL * x * a.x % mod); } ModInt operator / (const ModInt &a) const { return *this * a.inv(); } void operator += (const ModInt &a) { x += a.x; if (x >= mod) x -= mod; if (x < 0) x += mod;} void operator -= (const ModInt &a) { x -= a.x; if (x < 0) x += mod; if (x >= mod) x -= mod;} void operator *= (const ModInt &a) { x = 1LL * x * a.x % mod; } void operator /= (const ModInt &a) { *this = *this / a; } friend ostream &operator<<(ostream &os, const ModInt &a) { return os << a.x;} ModInt pow(int n) const { ModInt res(1), mul(x); while(n){ if (n & 1) res *= mul; mul *= mul; n >>= 1; } return res; } ModInt inv() const { int a = x, b = mod, u = 1, v = 0; while (b) { int t = a / b; a -= t * b; swap(a, b); u -= t * v; swap(u, v); } if (u < 0) u += mod; return u; } }; typedef ModInt<mod> mint; mint fact[N],infact[N]; int n,k; void init() { fact[0] = infact[0] = 1; for(int i=1;i<=n;i++) fact[i] = fact[i-1]*mint(i); infact[n] = fact[n].inv(); for(int i=n-1;i;i--) infact[i] = infact[i+1]*mint(i+1); } mint C(int a,int b) { return fact[a]*infact[b]*infact[a-b]; } int main() { ios; cin>>n>>k; init(); mint ans = fact[n]; for(int i=1;i<=n-k;i++) { ans += ((C(n-i,k-1)*fact[n-k])*fact[k-1])*mint(n-k); ans += ((C(n-i,k)*fact[n-k-1])*fact[k])*mint(n-k); ans -= (((C(n-i,k-1)*fact[n-k-1])*fact[k-1])*mint(n-k))*mint(i-1); } cout<<ans<<'\n'; return 0; }
这篇关于P7322 「PMOI-4」排列变换的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-26大厂数据结构与算法教程:入门级详解
- 2024-12-26大厂算法与数据结构教程:新手入门指南
- 2024-12-26Python编程入门指南
- 2024-12-26数据结构高级教程:新手入门及初级提升指南
- 2024-12-26并查集入门教程:从零开始学会并查集
- 2024-12-26大厂数据结构与算法入门指南
- 2024-12-26大厂算法与数据结构入门教程
- 2024-12-26二叉树入门教程:轻松掌握基础概念与操作
- 2024-12-26初学者指南:轻松掌握链表
- 2024-12-26平衡树入门教程:轻松理解与应用