传送门
瞪一下样例发现,每次排好序都是先把
n
−
1
n-1
n−1 位排好序,再排第
n
n
n 位
如果我们令
f
n
−
1
f_{n-1}
fn−1 表示到
n
−
1
n-1
n−1 的所有排列的计数次数,然后算出第
n
n
n 位的新增贡献就可以递推了
手玩一下发现如果
a
n
=
n
a_n=n
an=n 不需要归位,否则需要
2
a
i
−
1
2^{a_i-1}
2ai−1 次
然后就可以枚举最后一个填什么转移,发现是个等比数列
f
i
=
i
∗
f
i
−
1
+
(
2
i
−
1
−
1
)
∗
(
i
−
1
)
!
f_i=i*f_{i-1}+(2^{i-1}-1)*(i-1)!
fi=i∗fi−1+(2i−1−1)∗(i−1)!
除以总方案就是期望
#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
cs int Mod = 1e9 + 7;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
int ksm(int a, int b){ int ans = 1; for(;b;b>>=1, a=mul(a,a)) if(b&1) ans=mul(ans, a); return ans;}
cs int N = 1e5 + 5;
int n, E[N], pw[N], fac[N];
int main(){
n = read();
pw[0] = fac[0] = 1;
for(int i = 1; i <= n; i++) fac[i] = mul(fac[i-1], i), pw[i] = add(pw[i-1], pw[i-1]);
for(int i = 1; i <= n; i++) pw[i] = add(pw[i], pw[i - 1]);
E[1] = 0;
for(int i = 2; i <= n; i++){
E[i] = add(mul(E[i-1], i), mul(fac[i - 1], pw[i - 2]));
} cout << mul(ksm(fac[n], Mod-2), E[n]); return 0;
}

本文深入探讨了一种递推算法的实现细节,通过分析排列组合的数学问题,提出了一种新颖的递推公式,用于计算特定排列的计数次数。文章详细介绍了如何通过枚举和等比数列的方法来解决这个问题,并提供了完整的C++代码实现,包括快速幂运算、阶乘计算和模运算等关键步骤。
(找规律)&spm=1001.2101.3001.5002&articleId=102951734&d=1&t=3&u=214045d4e6ae4cec8e3a93f36f9d3e1b)
192

被折叠的 条评论
为什么被折叠?



