洛谷P4071-[SDOI2016]排列计数 题解
发现很多题解都没有讲清楚这道题为什么要用逆元、递推公式怎么来的。 我,风雨兼程三十载,只为写出一篇好题解。 还是我来造福大家一下吧。
题目大意:
一个长度为 n 且 1~n 各出现一次的序列,希望在“序列中有且只有 m个数的值 等于 它的位置”条件下求出序列个数。答案对1000000007取模。
题目分析:
这道题也许是加强版的“装错了的信封”,在“装错了的信封”上搞搞比利就好。我们不妨设:
值等于位置的数字 称 稳定的
值不等于位置数字 称 不稳定的
稳定的数字由于要保证 值等于位置,故 稳定的数字从序列左到右的值是递增的
首先我们根据组合数的思想可以知道:
答案 = 稳定的挑选方法数 乘上 不稳定的挑选方法数 。
所以现在我们的目的改成了求出 稳定的挑选方法数 和 不稳定的挑选方法数 。
稳定的挑选方法数:
我们已经知道了 稳定的数字从序列左到右的值是递增的,那么我们只需要模拟是哪几个数是稳定的即可。
不难想到,数量其实就是 在 \(n\) 个数中 挑选 \(m\) 个数的方案数 。
这不就是组合数里面的 \(C_n^m\) 吗?有 \(C_n^m\) = \(\frac{n!}{m!(n-m)!}\)
搞定
不稳定挑选方法数(错排):
我们假设已经知道了哪 \(m\) 个数是稳定的,那么我们可以在数组中暂时只看不稳定的数。
也就是 错排
我们令数组 \(F_x\) 为 有 \(x\) 个数字,每个数字均为不稳定的方法数。
由于这些数是不稳定的,那么就没有值的大小递增关系,所以我们对于每一个 \(F_x\) ,都要进行分类讨论。
\begin{cases}
当 k 位于第n位& \text{此时的错排数为 $F_{n-2}$(因 k 的位置已知,即求余的 n-2 个数的错排数)}\\
当 k 不位于第n位& \text{此时的错排数为 $F_{n-1}$}
\end{cases}\]
于是对于每个 \(F_i\) ,有 \(F_i\) = \((i - 1)\times(F_{x-1} + F_{x-2})\)
所以最终答案为:(\(C_n^m \times\)\(F_{n-m}\))%MOD
具体操作思路:
我们先要初始化,对于 \(C_x^y\) 要初始化阶乘,对于 \(F_i\) 要递推。
然后对于每一组数据,套 (\(C_n^m \times\)\(F_{n-m}\))%MOD 即可。
但是(还没完):
由题意得,最终方案数可能很大(所以才要取模),我们在进行 答案累乘时,(\(C_n^m \times\)\(F_{n-m}\))%MOD 可能会爆精度,我们于是要用到:
逆元(inv)
何为逆元( \(inv\) )?
比如当有 \((a \div b) \% MOD\) 时,防止 \(b\) 过大而爆精度,将 \(a \div b\) 转化为某种简单的乘法。
若 \(c\) 为 \(b\) 的逆元,则有 \((b \times c) \equiv 1 (mod 模数)\)
那么 \((a \div b) \% MOD\) = \((a \div b) \times 1 \% MOD\) = \((a \div b) \times b \times c \% MOD\) = \((a \times c) \% MOD\)
即 (一个数 除以 另一个数)%模数 = (一个数 乘上 另一个数的逆元)%模数
如何将逆元应用到该题中
我们初始化逆元数组 \(inv\) , \(inv\) 为 阶乘的逆元
那对于固定的值于模数,那个值的逆元怎么求呢?
请你参考费马小定理
直接给出答案:对于 \(a \% 1000000007\) 的逆元,为 \(a^{1000000007-2}\)
搞个快速幂就好了
代码:
#include<bits/stdc++.h>
#include<cctype>
#pragma GCC optimize(2)
#define Max(a,f) a > b ? a : b
#define Min(a,b) a < b ? a : b
#define in(n) n = read()
#define out(a) write(a)
#define outn(a) out(a),putchar('\n')
#define New ll
#define ll long long
#define rg register
using namespace std;
namespace IO_Optimization//优化函数
{
inline New read()//快读
{
New X = 0,w = 0;
char ch = 0;
while(!isdigit(ch))
{
w |= ch == '-';
ch=getchar();
}
while(isdigit(ch))
{
X = (X << 3) + (X << 1) + (ch ^ 48);
ch = getchar();
}
return w ? -X : X;
}
inline void write(New x)//快输
{
if(x < 0) putchar('-'),x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace IO_Optimization;
const int mod = 1000000000 + 7;//模数
const int MAXN = 1000000 + 2;//数组大小常量
ll a[MAXN],f[MAXN],inv[MAXN];
// 阶乘 F数组 逆元数组
inline ll ksm(ll a,ll b){//快速幂函数,求逆元
ll ans = 1;
a %= mod;
while(b)
{
if(b & 1)
ans = (a * ans) % mod;
a = (a * a) % mod;
b >>= 1;
}
return ans;
}
int main()
{
a[0] = 1;//初始化
for(rg int i = 1;i <= MAXN; ++i)
{
a[i] = (a[i - 1] * i) % mod;//记得取模
inv[i] = ksm(a[i],mod - 2);//计算逆元
}
f[1] = 0,f[2] = 1,f[3] = 2;//初始化
for(rg int i = 4;i <= MAXN; ++i)
f[i] = ((i - 1) * (f[i - 1] + f[i - 2])) % mod;//递推公式
int T = read();//多数据输入
while(T--)
{
int n = read(),m = read(),k = n - m;//输入n、m,k纯属方便
if(!k)//如果n-m=0,那么方案数为1
{
puts("1");
continue;
}
if(m == 0)//如果没有稳定的数,那么答案直接等于F[n]
{
outn(f[n]);
continue;
}
if(k == 1)//如果n-m=1,则有0个方案
{
puts("0");
continue;
}
outn(((( a[n] * inv[k] ) % mod * inv[m]) % mod * f[k]) % mod);//输出
//上面是把除法改成乘法逆元了
}
return 0;
}
洛谷P4071-[SDOI2016]排列计数 题解的更多相关文章
- 洛谷 P4071 [SDOI2016]排列计数 题解
P4071 [SDOI2016]排列计数 题目描述 求有多少种长度为 n 的序列 A,满足以下条件: 1 ~ n 这 n 个数在序列中各出现了一次 若第 i 个数 A[i] 的值为 i,则称 i 是稳 ...
- 洛谷——P4071 [SDOI2016]排列计数(错排+组合数学)
P4071 [SDOI2016]排列计数 求有多少种长度为 n 的序列 A,满足以下条件: 1 ~ n 这 n 个数在序列中各出现了一次 若第 i 个数 A[i] 的值为 i,则称 i 是稳定的.序列 ...
- 洛谷P4071 [SDOI2016] 排列计数 [组合数学]
题目传送门 排列计数 题目描述 求有多少种长度为 n 的序列 A,满足以下条件: 1 ~ n 这 n 个数在序列中各出现了一次 若第 i 个数 A[i] 的值为 i,则称 i 是稳定的.序列恰好有 m ...
- 洛谷 P4071 [SDOI2016]排列计数
洛谷 这是一道组合数学题. 对于一个长为n的序列,首先我们要选m个使之稳定\(C^{m}_{n}\). 且要保证剩下的序列不稳定,即错排\(D_{n-m}\). 所以答案就是:\[ANS=C^{m}_ ...
- P4071 [SDOI2016]排列计数 题解
分析: 线性求逆元:https://blog.csdn.net/qq_34564984/article/details/52292502 代码: #include<cstdio> usin ...
- 洛谷 P2606 [ZJOI2010]排列计数 解题报告
P2606 [ZJOI2010]排列计数 题目描述 称一个\(1,2,...,N\)的排列\(P_1,P_2...,P_n\)是\(Magic\)的,当且仅当对所以的\(2<=i<=N\) ...
- BZOJ4517 & 洛谷4071:[SDOI2016]排列计数——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=4517 https://www.luogu.org/problemnew/show/P4071 求有 ...
- ●洛谷P2606 [ZJOI2010]排列计数
题链: https://www.luogu.org/problemnew/show/P2606题解: 组合数(DP),Lucas定理 首先应该容易看出,这个排列其实是一个小顶堆. 然后我们可以考虑dp ...
- 洛谷P2606 [ZJOI2010]排列计数
题目描述 称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很 ...
- 洛谷P2606 [ZJOI2010]排列计数(数位dp)
题目描述 称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很 ...
随机推荐
- python itertools 用法
1.介绍itertools 是python的迭代器模块,itertools提供的工具相当高效且节省内存.使用这些工具,你将能够创建自己定制的迭代器用于高效率的循环.- 无限迭代器 itertools包 ...
- Django--评论功能实现和用户登录
1.确定实现评论功能的方式 1.第三方社会化评论插件(有言.多说.网易云跟帖等) 优点:可以直接用,紧急时开发快 缺点:评论存储在第三方,第三方挂了就不弄用 2.Django评论库 django-co ...
- 111-PHP类变量之间的赋值标识为同一个对象
<?php class mao{ //定义猫类 public $age=0; //定义多个属性并初始化 public $weight=50; public $color='white'; } $ ...
- gem5-gpu全系统模式
# 注意:安装好gem5-gpu后再配置全系统环境 # 下载全系统模拟需要的工具,详见http://gem5.org/Running_gem5#Full_System_.28FS.29_Mode,将L ...
- 【Android】家庭记账本手机版开发报告六
一.说在前面 昨天 1.创建登入和注册界面:2.向数据库添加一张用户表 今天 用图标显示账单情况 问题 1.使用第三方库 hellochart,时添加依赖构建失败 2.在 chertFragmen ...
- HDU - 2602 Bone Collector(01背包讲解)
题意:01背包:有N件物品和一个容量为V的背包.每种物品均只有一件.第i件物品的费用是volume[i],价值是value[i],求解将哪些物品装入背包可使价值总和最大. 分析: 1.构造二维数组: ...
- BlackArch Linux 2019.06.01 宣布发布
导读 BlackArch Linux是一个基于Arch Linux的发行版,专为渗透测试人员和安全研究人员设计,并包含大量渗透测试和安全实用程序,已宣布发布2019.06.01版本. BlackArc ...
- 12 —— node 获取文件属性 —— 利用 自调用 闭包函数 解决 i 丢失的问题
闭包的作用 : 保存变量 一,i 丢失的案例 var arr = ['node','vue','mysql'] for(var i=0;i<arr.length;i++){ setTimeout ...
- blkid命令 获取文件系统类型、UUID
在Linux下可以使用blkid命令对查询设备上所采用文件系统类型进行查询.blkid主要用来对系统的块设备(包括交换分区)所使用的文件系统类型.LABEL.UUID等信息进行查询.要使用这个命令必须 ...
- 04-String——课后动手动脑
1.请运行以下示例代码StringPool.java,查看输出结果.如何解释这样的输出结果?从中你能总结出什么? public class StringPool { public static voi ...