汕頭市隊賽SRM 20 T3 靈魂覺醒
背景
自從芽衣、布洛妮婭相繼靈魂覺醒之後,琪亞娜坐不住了。自己可是第一個入駐休伯利安號的啊!於是她打算去找德麗莎幫忙,為她安排了靈魂覺醒的相關課程。
第一天,第一節課。
“實現靈魂覺醒之前,你需要先將自己的崩壞能按順序排好……”
“誒誒,這個要怎麽做呢?”算法課沒認真聽講也是沒有辦法的嘛。
於是,琪亞娜設(xia)計(bian)了一套自己的排序方法。
描述
我們可以用n張卡片代表崩壞能,上面恰好寫了1到n。
一開始這些卡片是隨機排列的,然後為了把它們從小到大排好序,進行如下操作:
①如果卡片已經按1到n排好序,結束操作。
②觀察現有排列,如果某一個大於1的區間內的數是順序加一的,就把這個區間內的卡片粘起來,它們在以後的操作中不會分開。(比如15234這個排列會把234粘起來)
③把所有卡片隨機排列(粘在一起的不分開),然後回到①。
恰好路過的符華對這種排序方法非常感興趣,希望知道它的期望復雜度。於是求期望進行③操作的次數的任務就交給你了。答案對1e9+7取模。
輸入格式
一個不超過2000的整數n。
輸出格式
一個整數,答案對1e9+7取模後的結果。
樣例輸入
3
樣例輸出
333333338
數據範圍與約定
測試點 | n |
1 |
1 |
2 | 5 |
3 | 8 |
4 | 10 |
5 | 13 |
6 | <=1000 |
7 | <=1000 |
8 | <=2000 |
9 | <=2000 |
10 | <=2000 |
樣例解釋
n=3時的精確值是
後記:
琪亞娜·卡斯蘭娜:你們是怎麽排序的啊?
雷電芽衣:有個庫函數叫sort來著……
布洛妮婭:對,挺快的。
————————————————————————————
這題一開始我想著暴力 ans[x]=sigma g[x][y]*ans[y]
g[x][y]表示長度為x的排列 排一次之後縮成y段的概率
g[x][y]可以暴力算 不過後來打表發現是個組合數 然後就可以A辣
#include<cstdio> #includeView Code<cstring> #include<algorithm> #define LL long long const int M=2e3+7,mod=1e9+7; int read(){ int ans=0,f=1,c=getchar(); while(c<‘0‘||c>‘9‘){if(c==‘-‘) f=-1; c=getchar();} while(c>=‘0‘&&c<=‘9‘){ans=ans*10+(c-‘0‘); c=getchar();} return ans*f; } int n,k; int g[M][M]; LL w[M],inv[M]; LL pmod(LL a,LL b){ int ans=1; while(b){ if(b&1) ans=ans*a%mod; b>>=1; a=a*a%mod; } return ans; } void prepare(){ w[0]=1; for(int i=1;i<=n;i++) w[i]=w[i-1]*i%mod; inv[n]=pmod(w[n],mod-2); for(int i=n;i;i--) inv[i-1]=inv[i]*i%mod; } int f[M],v[M],s[M],ans[M]; void P(int &x){x=(x%mod+mod)%mod;} int main(){ n=read(); prepare(); for(int j=1;j<=n;j++){ g[j][j]=w[j]-s[j]; P(g[j][j]); for(int i=j+1;i<=n;i++){ g[i][j]=g[j][j]*(w[i-1]*inv[j-1]%mod*inv[i-j]%mod)%mod; P(g[i][j]); s[i]+=g[i][j]; P(s[i]); } } ans[1]=0; for(int i=2;i<=n;i++){ LL ly=0; for(int j=2;j<i;j++) ly=(ly+1LL*g[i][j]*inv[i]%mod*(ans[j]+1)%mod)%mod; ans[i]=((ly+g[i][i]*inv[i]%mod)*pmod((1-g[i][i]*inv[i]%mod+mod)%mod,mod-2))%mod; }printf("%d\n",ans[n]); return 0; }
汕頭市隊賽SRM 20 T3 靈魂覺醒