日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[SDOI2016]排列计数 (错排数概念 + 递推公式【附带证明】)

發布時間:2023/12/3 编程问答 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [SDOI2016]排列计数 (错排数概念 + 递推公式【附带证明】) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

辛勤二更

  • 題目
  • 題解
    • 錯排數概念
    • 錯排數遞推公式及其證明
  • 代碼實現

這種題做的時候:
做完后:正常這就是生活,我們要學會習慣

題目

求有多少種長度為 n 的序列 A,滿足以下條件:
1 ~ n 這 n 個數在序列中各出現了一次
若第 i 個數 A[i] 的值為 i,則稱 i 是穩定的。序列恰好有 m 個數是穩定的
滿足條件的序列可能很多,序列數對 10^9+7取模。

輸入格式
第一行一個數 T,表示有 T 組數據。
接下來 T 行,每行兩個整數 n、m。
輸出格式
輸出 T 行,每行一個數,表示求出的序列數

輸入輸出樣例
輸入
5
1 0
1 1
5 2
100 50
10000 5000
輸出
0
1
20
578028887
60695423
說明/提示
測試點 1 ~ 3:T=1000, n≤8, m≤8;
測試點 4 ~ 6: T=1000, n≤12,m≤12;
測試點 7 ~ 9: T=1000, n≤100, m≤100;
測試點 10 ~ 12: T = 1000,n≤1000, m≤1000;
測試點 13 ~ 14: T = 500000, n≤1000, m≤1000;
測試點 15 ~ 20: T = 500000, n≤1000000,m≤1000000。

題解

突然想感慨一番:

步入正軌↓(我真的很討厭這種有模數的方案數題)

題意很簡單,n個數固定m個數,那么就有n-m個數上的位置是不穩定的
也就是這n-m個數要滿足A[i]≠i,對于這n-m中的某個位置i,就只有n-m-1種填法


錯排數概念

這里就引入錯排數的概念

n個有序的元素應有n!個不同的排列,如若一個排列使得所有的元素不在原來的位置上,則稱這個排列為錯排;有的也稱之為重排

錯排數遞推公式及其證明

求n個數的錯排數:DP[n]=(n?1)?(DP[n?1]+DP[n?2])DP[n]=(n-1)*(DP[n-1]+DP[n-2])DP[n]=(n?1)?(DP[n?1]+DP[n?2])
證明如下
①考慮第n個元素,把它放在某一個位置,比如位置k,一共有n-1種放法
②考慮第k個元素,這時有兩種情況:
(1)把它放到位置n,那么對于除n以外的n-1個元素,由于第k個元素放到了位置n,所以剩下n-2個元素的錯排即可,有f(n?2)f(n-2)f(n?2)種放法;
(2)第k個元素不放到位置n,這時對于這n-1個元素的錯排,有f(n?1)f(n-1)f(n?1)種放法
運用加法以及乘法原理,得證完畢。。。


處理完n-m后,我們就要處理m,要知道選的m的位置不同算不同的方案哦(⊙o⊙)!
這個其實就是排列組合,從n個數中選m個的方案數:
Cnm=n!m!?(n?m)!C_n^m=\frac{n!}{m!*(n-m)!}Cnm?=m!?(n?m)!n!?

這里寫出來后就發現這里面涉及到了除法,而取模運算中是不能進行除法運算的
所以我們就要去求m!m!m!(n?m)!(n-m)!(n?m)!各自的逆元與n!n!n!相乘,
有很多方法都可以完成,費馬小定理,擴展歐幾里得…


代碼實現

#include <cstdio> #define mod 1000000007 #define LL long long #define MAXN 1000000 int T, n, m; LL sum[MAXN + 5], inv[MAXN + 5], dp[MAXN + 5]; LL qkpow ( LL x, int y ) {LL ans = 1;while ( y ) {if ( y & 1 )ans = ans * x % mod;x = x * x % mod;y >>= 1;}return ans % mod; } LL getinv ( LL x ) {return qkpow ( x, mod - 2 ); } int main() {scanf ( "%d", &T );sum[0] = 1;inv[0] = 1;for ( int i = 1;i <= MAXN;i ++ ) {sum[i] = sum[i - 1] * i % mod;inv[i] = getinv ( sum[i] );}dp[0] = dp[2] = 1;for ( int i = 3;i <= MAXN;i ++ )dp[i] = ( dp[i - 1] + dp[i - 2] ) % mod * ( i - 1 ) % mod;while ( T -- ) {scanf ( "%d %d", &n, &m );printf ( "%lld\n", sum[n] * inv[n - m] % mod * inv[m] % mod * dp[n - m] % mod ); }return 0; }

byebye~~~~~~

總結

以上是生活随笔為你收集整理的[SDOI2016]排列计数 (错排数概念 + 递推公式【附带证明】)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。