JZOJ 5931. 【NOIP2018模拟10.27】冒泡排序
Description
題目背景
冒泡排序的交換次數被定義為交換過程的執行次數。
題面描述
小 S 開始專注于研究?度為 n 的排列,他想知道,在你運氣足夠好的情況下(即每次冒泡排序的交換次數都是可能的最少交換次數,仿佛有上帝之手在操控),對于一個等概率隨機的長度為n 的排列,進行這樣的冒泡排序的期望交換次數是多少?
Input
從文件 inverse.in 中讀入數據。
輸入第一行包含一個正整數 T ,表示數據組數。
對于每組數據,第一行有一個正整數,保證 n ≤ 10^7 。
Output
輸出到文件 inverse.out 中。
輸出共 T 行,每行一個整數。
對于每組數據,輸出一個整數,表示答案對 998244353 取模的結果。
Sample Input
2
2
4
樣例 2
見選手目錄下的 inverse/inverse2.in 與 inverse/inverse2.ans 。
Sample Output
499122177
415935149
Data Constraint
Solution
-
這題的方法非常多,各路大佬各顯神通……
-
我還是從最簡單的講起吧:
方法①:
-
一個排列要變成 111 到 nnn,交換的方法肯定是沿著環來互換。
-
一個大小為 kkk 的環,就需要 k?1k-1k?1 步交換來達到有序。
-
顯然環越多所需的交換次數越少,若有 ppp 個環,則答案為 n?pn-pn?p 。
-
于是設 f[i]f[i]f[i] 表示這 iii 個點形成環的期望個數。
-
則有轉移:f[i]=f[i?1]+1if[i]=f[i-1]+\frac{1}{i}f[i]=f[i?1]+i1?
-
(第 iii 個點可能自成一環,也可能被安排進前 i?1i-1i?1 的那些環內)
-
于是我們 O(n)O(n)O(n) 預處理出 f[i]f[i]f[i] ,每讀入一個 nnn 輸出 n?f[n]n-f[n]n?f[n] 即可。
-
注意求 111 到 nnn 的倒數不要每次快速冪,會超時,應該 O(n)O(n)O(n) 線性求。
-
不會的話可以看看這里:O(N) 求 1~N 逆元 模板及證明
-
于是我們就 O(n)O(n)O(n) 解決本題啦!
方法②:
-
現在來講 pty 提供的解法!
-
仍然考慮環的貢獻,枚舉環的大小 iii ,單獨算貢獻,則答案即為: ∑i=2nCni?(i?1)!?(n?i)!?(i?1)\sum_{i=2}^{n}C_{n}^{i}*(i-1)!*(n-i)!*(i-1)i=2∑n?Cni??(i?1)!?(n?i)!?(i?1)
-
其中 CniC_n^iCni? 表示從 nnn 中選 iii 個點形成環,(i?1)!(i-1)!(i?1)! 表示這 iii 個點隨便排(注意不是 i!i!i! ,環的方向可以變),(n?i)!(n-i)!(n?i)! 表示剩下 n?in-in?i 個點隨便排,(i?1)(i-1)(i?1) 表示大小為 iii 的環需要交換 (i?1)(i-1)(i?1) 次。
-
那么上式可化為:∑i=2ni?1i\sum_{i=2}^{n}\frac{i-1}{i}i=2∑n?ii?1?
-
即:n?f[n]n-f[n]n?f[n] ,兩種方法算出來的結果是一樣的!
方法③:
-
當然還有一些對數字十分敏感的同學都能找到規律了。
-
設答案中分子為 ana_nan? ,則分母為 n!n!n! ,可以發現關于 ana_nan? 的遞推式:an=nan?1+(n?1)(n?1)!a_n=na_{n-1}+(n-1)(n-1)!an?=nan?1?+(n?1)(n?1)!
-
發現方法玄學,這里不再展開,有興趣的同學可以課下研究……
Code
#include<cstdio> #include<cctype> using namespace std; typedef long long LL; const int N=1e7+5,mo=998244353; int f[N]; inline int read() {int 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; } void write(int x) {if(x>9) write(x/10);putchar(x%10+'0'); } int main() {freopen("inverse.in","r",stdin);freopen("inverse.out","w",stdout);f[0]=f[1]=1;for(int i=2;i<N;i++) f[i]=(LL)(mo-mo/i)*f[mo%i]%mo;for(int i=2;i<N;i++) f[i]=(f[i]+f[i-1])%mo;int T=read();while(T--){int n=read();write((n-f[n]+mo)%mo),putchar('\n');}return 0; } 與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的JZOJ 5931. 【NOIP2018模拟10.27】冒泡排序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JZOJ 5933. 【NOIP2018
- 下一篇: JZOJ 5938. 【NOIP2018