fzu 2282 - Wand 组合数学 乘法逆元的三种求法 错排公式

0.序

Wand

题目大概意思是给出 n 组对应关系, 将它们打乱, 求最后至少有 k 组对应关系正确的打乱方式
思路是从 k 到 n 枚举正确的对应关系个数, 求组合数 Cn(k) * 剩下 n - k 个对应关系完全错误的排法

1.排错公式

当 n 个编号元素放在 n 个编号位置,元素编号与位置编号全都不对应的方法数用 dp[n] 表示
显然 dp[1] = 0, dp[2] = 1;
将 n 个元素错排则

  • (1) 将第 1 个元素放到错误的 (n - 1) 个位置
  • (2) 假设(1)中将第 1 个元素放在了 k 位置, 那么考虑两种情况:
  1. 将第 k 个元素放到第 1 个位置, 等价于将第 1 个元素与第 k 个元素交换, 递推考虑错排 n- 2 个元素的方法, 为dp[n - 2]种方法.
  2. 将第 k 个元素放到 n - 2 个其他的除了第 1 个位置以外的错误位置, dp[n - 1]种方法.

=> dp[n] = (n - 1)(dp[n - 1] + dp[n - 2])

2. 乘法逆元

  • 扩展欧几里德算法
    用于求出一组 x 和 y 满足方程 ax+by = gcd(a, b)
inline long long ExGCD(long long A, long long B, long long& x, long long& y){
    if(A == 0 && B == 0) return -1;
    if(B == 0){x = 1, y = 0; return A;}
    long long d = ExGCD(B, A % B, y, x);
    y -= A / B * x;
    return d;
}

因为题目给的要取余的数 MOD 一般是 1e9 + 7 之类的大质数所以 gcd(a, MOD) == 1
即可求出乘法逆元

long long ModReverse(long long a, long long f){
    long long x, y, d = ExGCD(a, f, x , y);
    if(d == 1){
        if(x % f <= 0) return x % f + f;
        else return x % f;
    }
    return -1;
}
  • 费马小定理
    MOD 为素数时, a ^ (MOD - 1) == 1(mod MOD), 所以 a ^ (MOD - 2)即为 a 模 MOD 意义下的乘法逆元, 快速幂取模即可.

  • 时间复杂度为 O(n) 的递推求 [1, n] 的逆元表(n < MOD)

推导

假设前 i - 1 个数的乘法逆元都已知
由 MOD mod i + (MOD div i) * i = MOD
=> MOD mod i + (MOD div i) * i = 0 (mod MOD)
=> (MOD div i) * i = - (MOD mod i) (mod MOD)
=> i ^ -1 = - (MOD div i) / (MOD mod i)
=> i ^ -1 = - (MOD div i) * (MOD mod i) ^ -1
因为 MOD mod i 小于 i 所以 (MOD mod i) ^ -1 是已知的, 即可得到以下递推关系

inv[1] = 1;
for(int i = 2; i <= 10000; i++) inv[i] =  inv[MOD % i] * (MOD - MOD / i) % MOD;

3. 组合数

直接公式:

递推公式:

3. AC代码

逆元表写的, 比较喜欢打表预处理

#include <iostream>
#include <cstdio>

using namespace std;

const long long MOD = 1e9 + 7;

long long dp[10007], inv[10007];

int main()
{
    int T, n, k;
    long long C, ans;

    scanf("%d", &T);
    dp[1] = 0, dp[2] = 1, inv[1] = 1;
    for(int i = 3; i <= 10000; i++) dp[i] = ((dp[i - 1] + dp[i - 2]) % MOD) * (i - 1) % MOD;
    for(int i = 2; i <= 10000; i++) inv[i] =  inv[MOD % i] * (MOD - MOD / i) % MOD;
    for(int cas = 1; cas <= T; cas++){
        scanf("%d%d", &n, &k);
        ans = 1, C = n;
        for(int i = 2; i <= k; i++) C = ((C * (n - i + 1) % MOD) * inv[i]) % MOD;
        for(int i = k; i < n; i++){
            ans = (ans + (C * dp[n - i]) % MOD ) % MOD;
            C = ((C * (n - i) % MOD) * inv[i + 1]) % MOD;
        }
        printf("%I64d\n", ans);
    }
    return 0;
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352

推荐阅读更多精彩内容