【组合数】Subpermutation-[2021中国大学生程序设计竞赛(CCPC)- 网络选拔赛(重赛)]
题目链接(http://acm.hdu.edu.cn/showproblem.php?pid=7133)
Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 262144/262144 K (Java/Others)
Problem Description
A permutation of is a sequence of length in which each number from to appears exactly once. A of is a sequence that connects all permutations of 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:
- : the full-permutation of . For example, .
- : the set of all permutations of . For example, .
- : the number of contiguous subsequences in that are equal to . For example, .
Now given and , please calculate modulo .
Input
The first line contains one integer , indicating the number of test cases.
The only line for each case contains two integers and , as described in the description.
Output
For each test case, output a single integer modulo .
Sample Input
4
2 1
2 2
3 2
4 3
Sample Output
2
2
4
15
Hint
For the third case in the sample, , . There are contiguous subsequences in that are equal to or : .
题意
把所有元排列按字典序从小到大排成一列,求其中有多少子串为元{1 ~ m}排列,模,组询问,()
思路
代码很简短,思路清楚比较重要,补题怕自己忘了所以记录一下(来自emo老师讲题,自己做的笔记)
分解子问题:
子问题一:在一个完整的元排列里面有元排列为子串的个数
假设为元排列,里面有一个子串是元排列。元排列的元素紧凑在一起,所以它自己的元素的排列方式有种,把它看成一个小团。{1~m}共包含个数,剩下来的数共个,剩下的数各为小团,把所有小团进行一个排列,所以总排列数为 元排列小团的排列数 * 所有小团的排列数,即
子问题二(难点):元排列有可能穿插在了两个相邻的元排列中
排列{} 或者{} 中前两个元排列中也出现了合法的元排列
这时候就要思考一下两个相邻的元排列有什么性质
随便列举几个相邻排列
{}{} {}{}
我们会发现对于元排列 可以找到
(是最后一个满足 的下标)
它的下一个排列中的,会被原排列中的一个 ()替换掉,后面的元素倒着排列
那么下一个排列为
它符合的性质是
如果一个元排列插在两个元排列中间,从下标开始,
①() 该排列构造:
思考这里有多少种排列方式
首先剩下的个元素随便排,这部分是,元序列里到本应随便排,但要求到在前一个元排列中,一定要出现一个这样的数对才行,否则不合法,所以剩下的部分是减去不合法的情况,即减去这种情况,即
所以方案数 =
②()该排列构造:从的位置到下一个序列 (不敲了,荧光笔范围)
首先到一定是到里面最小的个数,对这两个序列进行分析,发现要大于,所以如果在到的序列里那必定在里面,这和的范围不符,因而只有一种可能就是不在到的序列里,则最后一位的下标,故这一情况的排列方式可以像上一种那样算出来
首先到怎么选,第一个排列里,规则同上,为
剩下的数里,由于一定要包含,所以剩下的数不能完全呈单调递减,需要减去单调递减一情况,为
所以方案数 =
总结来说,横跨两个元排列的方案数为
再进行一些推导,发现
这两个颜色不同的地方是可以相互抵消的
那么化简就可得方案数 =
=
=
=
这样,预处理之后,O(1)即可得横跨两个元排列的方案数
而一个排列内的方案数,预处理下阶乘即可,两种排列方式的方法相加即得答案
AC代码
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3f
#define int long long
#define ull unsigned long long
#define PII pair<int,int>
using namespace std;
typedef long long ll;
const int N = 1e6 + 100;
const int mod = 1e9 + 7;
int t, n, m;
int a[N];
int fact[N], invfact[N];
int quickpow(int a, int b) {//快速幂板子
int res = 1;
while (b > 0) {
if (b & 1) res = res * a % mod;
b >>= 1;
a = a * a % mod;
}
return res;
}
void init() {//预处理
fact[0] = 1;
for (int i = 1; i < 1000001; i++) {
fact[i] = fact[i - 1] * i % mod;
}
invfact[1000000] = quickpow(fact[1000000], mod - 2);
for (int i = 1000000; i; i--) {
invfact[i - 1] = invfact[i] * i % mod;
}
for (int i = 1; i < 1000001; i++) {
invfact[i] = (invfact[i - 1] + invfact[i]) % mod;
}
return;
}
void solve() {
cin >> n >> m;//输入 n m
cout << (fact[m] * (n * fact[n - m] % mod - invfact[m - 1] + invfact[0] + mod) % mod) << endl;//直接输出公式求得答案
return;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
init();
while (cin >> t) {
while (t--) {
solve();
}
}
return 0;
}
/*
i raised a cute kitty in my code,
my friend who pass by can touch softly on her head:)
/l、
Meow~(゚、 。7
|、 ~ヽ
じしf_,)ノ
*/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南