
题目大意:
n个数,共有n!种排列方式,记Pi(a)表示序列a的第i种排队方式,cnt(Pi(a))表示P(i)的逆序对个数,PLMM想知道这n!种排列方式共有多少对逆序对
给定一个 nnn 个数,在所有排列顺序中,每种排列的分别的逆序对总数的总数是多少
思路:
对于互不相等的两数 (a,b),满足 a>b,显然 (a,b)成为逆序对,当且仅当a出现在b之前。所以我们注意到:在 n!次排列下,要么 a 在 b 前面,要么 b 在 a 前面,且两种情况出现的概率都为 1/2。所以我们对于互不相同的一组 (a,b),计算 (a,b) 的所有排列情况并乘上 1/2即可。
具体如何计算(a,b)对答案的贡献呢?
- 对于 a,在未确定位置的 n 个数中它的摆放可以是 n 种
- 对于b,在未确定位置的n-1个数种它的摆放可以是n-1种
- 所以a,b可以放置的总方案数位n*(n-1),但是需要除以二,因为我们只要大数在前
- 剩下的n-2个数,全排列即可(n-2)!
由于对于每一个不同的对,处理情况都是一样的,所以只需要统计出来有多少不同的对数,最终乘上一个(n)*(n-1)/2*(n-2)!即可
- #include
- using namespace std;
- #define int long long
- const int N=1e5+5,mod=1e9+7;
-
- int n,w[N];
- int f[N*2];
-
- signed main() {
- f[1]=1;
- for(int i=2; i<=N; i++) {
- f[i]=f[i-1]*i%mod;
- }
-
- cin>>n;
- for(int i=1; i<=n; i++) {
- cin>>w[i];
- }
-
- sort(w+1,w+1+n);
-
- if(n==1) {
- cout<<0;
- } else {
- int cnt=0;//记录不同的对数
- for(int i=1; i<=n; i++) {
- int fid=lower_bound(w+1,w+1+n,w[i])-(w+1);//找到第一个大于等于它的数的下标(0------n-1)
- //找到的这个fid就是前面有多少个小于它的
- cnt+=fid;
- }
-
- int put=(n*(n-1)/2)%mod*f[n-2]%mod;
- //意思是,对于每个对,先找两个位置安排两个数,C(n,2),但是我们只要
- //大数在前的情况,所以除2,另外的数随便安排,这样这个对对答案能贡献多少就算出来了
- //一个有cnt对,所以把所有对加起来就行了
- //因为每个对对答案的贡献是一样的
- //所以
- cout<<(cnt*put+mod)%mod;
- }
- return 0;
- }