小明和他的逆序对数
题目描述:
Tom学会了通过写程序求出一个1-n的排列的逆序对数,但他的老师给了他一个难题:
给出一个1-n的排列,求所有字典序比它小的1-n的排列的逆序对数之和。
Tom一时不知道该怎么做,所以他来找你帮他解决这个问题。
因为数可能很大,答案对109+7取模。
输入情况:
输入包含多组数据(大约20组)。对于每一组数据,第一行一个正整数n,第二行n个数,是一个n的排列。
n≤100
题解:
字典序比它小,有点类似于数位dp的dfs写法。只考虑当前位,之前的都已经考虑过,然后当前位可以不顶着放,于是后面的排列数就是阶乘,并且提前用dp预处理出i个排列数的逆序对总和,那么就只有他本身提供的逆序对数了,然后看当前还能用的有几个比它小的,然后就会算了。之后是顶着它放数,后面的对ans的逆序对影响留到后面的dfs中去改,而后面一共有多少种放法用dfs的返回值来得到。
重点:
关键是一位一位的放,分成顶着放和不顶着放,然后看情况需要什么搞出来什么。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(ll i = a;i < b;i++)
#define REP_D(i, a, b) for(ll i = a;i <= b;i++)
typedef long long ll;
using namespace std;
const ll maxn = 1e2 + 10;
const ll M = 1e9 + 7.4;
ll f[maxn], jiecheng[maxn];
ll n;
ll vis[maxn], a[maxn];
ll ans;
ll key;
ll pow_mod(ll x, ll n)
{
if(n==0)
return 1;
ll xx = x*x%(M);
ll nn = n/2;
ll ans = pow_mod(xx, nn);
if(n%2==1)
{
ans = (ans*x)%(M);
}
return ans;
}
void getKey()
{
key = pow_mod(2, M-2);
}
ll dfs(ll pos)//整到题的关键在这。
{
if(pos >= n + 1)//因为要严格小于
return 0;
vector<ll> xiao;//有几个比它小的并且没用的
REP_D(i, 1, a[pos] - 1)
{
if(vis[i]==0)
{
xiao.push_back(i);
}
}
ll num = 0;
REP(i, 0, xiao.size())//算没有顶着来的情况
{
//ll x = xiao[i];
ans = ((ans + f[n-pos])%M + i*jiecheng[n-pos]%M)%M;//直接改ans
num = (num + jiecheng[n-pos])%M;
}
vis[a[pos]] = 1;
ll tmp = dfs(pos + 1);//之后的会在dfs中改
num = (num+tmp)%M;
ans = (ans + xiao.size()*tmp%M)%M;//当前位的影响
return num;
}
void getJiecheng()
{
//ll tmp = 1;
jiecheng[0] = 1;
jiecheng[1] = 1;
for(ll i = 2; i <= 100; i++)
{
jiecheng[i] = (jiecheng[i-1]*i)%M;
}
}
void getF()
{
f[1] = 0;
f[2] = 1;
REP_D(i, 3, 100)
{
f[i] = (f[i-1]*i%M + jiecheng[i-1]*i%M*(i-1)%M*key%M)%M;
//printf("--%I64d--- %I64d\n", i, f[i]);
}
}
void solve()
{
ans = 0;
CLR(vis);
dfs(1);
printf("%I64d\n", ans);
}
ll readin()
{
char t;
t = getchar();
while(!isdigit(t))
t = getchar();
ll ans = 0;
while(isdigit(t))
{
ans = ans*10 + t-'0';
t = getchar();
}
return ans;
}
int main()
{
freopen("2Bin.txt", "r", stdin);
//freopen("3Bout.txt", "w", stdout);
getKey();
getJiecheng();
getF();
while(scanf("%I64d", &n) != EOF)
{
REP_D(i, 1, n)
{
ll t = readin();
a[i] = t;
}
solve();
}
return 0;
}