Codeforces 396D On Sum of Number of Inversions in Permutations (逆序对计数)

#include <iostream>  
#include <cstdio>  
#include <cstring>  
#include <cmath>  
#include <algorithm>  
#include <queue>  
#include <set>  
#include <ctime>  
#include <cstdlib>  
using namespace std;  


#define inf 0x3f3f3f3f
#define N 1000020
#define M 1000020
#define LL long long
#define mod 1000000007
#define ls (i << 1)
#define rs (ls | 1)
#define md (ll + rr >> 1)
#define lson ll, md, ls
#define rson md + 1, rr, rs
#define B 350


int n, a[N];
int s[N<<2];
int f[N], g[N];

int qpow(int x, int k) {
	int ret = 1;
	while(k) {
		if(k & 1) ret = 1LL * ret * x % mod;
		k >>= 1;
		x = 1LL * x * x % mod;
	}
	return ret;
}
int inv4 = qpow(4, mod - 2);
int calc(int k) {
	int ret = f[k];
	ret = 1LL * ret * k % mod * (k - 1) % mod * inv4 % mod;
	return ret;
}

void update(int x, int v, int ll, int rr, int i) {
	s[i] += v;
	if(ll == rr) return;
	if(x <= md) update(x, v, lson);
	else update(x, v, rson);
}
int query_0(int l, int r, int ll, int rr, int i) {
	if(ll == l && rr == r) return r - l + 1 - s[i];
	if(r <= md) return query_0(l, r, lson);
	if(l > md) return query_0(l, r, rson);
	return query_0(l, md, lson) + query_0(md + 1, r, rson);
}
int query_1(int l, int r, int ll, int rr, int i) {
	if(ll == l && rr == r) return s[i];
	if(r <= md) return query_1(l, r, lson);
	if(l > md) return query_1(l, r, rson);
	return query_1(l, md, lson) + query_1(md + 1, r, rson);
}


int main() {
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i) {
		scanf("%d", &a[i]);
		a[i]--;
	}
	f[0] = 1;
	for(int i = 1; i <= n; ++i) f[i] = 1LL * f[i-1] * i % mod;
	for(int i = 1; i <= n; ++i) {
		int t = query_1(0, a[i], 0, n - 1, 1);
		update(a[i], 1, 0, n - 1, 1);
		a[i] -= t;
	}
	g[n+1] = g[n] = 1;
	for(int i = n - 1; i >= 1; --i) {
		g[i] = 1LL * a[i] * f[n-i] % mod + g[i+1];
		if(g[i] >= mod) g[i] -= mod;
	}
	int ans = 0;
	for(int i = 1; i <= n; ++i) {
		int k = n - i;
		ans += 1LL * a[i] * calc(k) % mod;
		if(ans >= mod) ans -= mod;
		ans += 1LL * a[i] * (a[i] - 1) / 2 % mod * f[k] % mod;
		if(ans >= mod) ans -= mod;
		ans += 1LL * a[i] * g[i+1] % mod;
		if(ans >= mod) ans -= mod;
	}
	printf("%d\n", ans);
	return 0;
}
		

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值