数学(乘法逆元)hdu5225

Tom and permutation

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 201    Accepted Submission(s): 87


Problem Description
Tom has learned how to calculate the number of inversions in a permutation of n distinct objects by coding, his teacher gives him a problem:
Give you a permutation of n distinct integer from 1 to n, there are many permutations of 1-n is smaller than the given permutation on dictionary order, and for each of them, you can calculate the number of inversions by coding. You need to find out sum total of them.
Tom doesn't know how to do, he wants you to help him.
Because the number may be very large, output the answer to the problem modulo 109+7 .
 

Input
Multi test cases(about 20). For each case, the first line contains a positive integer n, the second line contains n integers, it's a permutation of 1-n.
n100
 

Output
For each case, print one line, the answer to the problem modulo 109+7 .
 

Sample Input
  
  
3 2 1 3 5 2 1 4 3 5
 

Sample Output
 
 
1 75
题意:Tom学会了通过写程序求出一个1-n的排列的逆序对数,但他的老师给了他一个难题:
给出一个1-n的排列,求所有字典序比它小的1-n的排列的逆序对数之和。
Tom一时不知道该怎么做,所以他来找你帮他解决这个问题。
因为数可能很大,答案对109+7取模。

官方题解:从1到n枚举k,表示当前要计算的排列与读入的排列前k-1项相同,而第k项不同。对于每一个k,再枚举一个t,表示当前要计算的排列的第k项是t,所以t要比读入的排列的第k项小,并且不与前k-1个数中的任意一个数相等。
那么,剩下的n-k个数任意排列,都满足字典序小于读入的排列。所以要计算它们的逆序对数之和。可以分情况计算:
1、逆序对中的两个数都在前k-1个位置,可以对于每一个k都暴力计算。
2、逆序对中的一个数在前k-1个位置,另一个数不在,同样可以对于每一个k都暴力计算。
3、逆序对中的一个数在第k个位置,另一个数在后n-k个位置。也可以暴力计算。
4、逆序对中的两个数都在后n-k个位置。这个值可以DP预处理,也可以推出一个式子直接计算。
可以这样考虑:在后n-k个位置中,有一半的排列方式中,第i小的数在第j小的数(i>j)的前面。共有(n-k)!种排列方式,所以对于一对数,有(n−k)!2种排列方式中是逆序对。共(n−k)⋅(n−k−1)/2对数,所以这类逆序对共(n−k)⋅(n−k−1)⋅(n−k)!/4对。
时间复杂度:O(n3)

逆元讲解

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=110;
const int MOD=1e9+7;
typedef long long LL;
int N;
int a[maxn];
int vis[maxn],vis1[maxn],buf[maxn];
LL f[maxn];
void init()
{
    f[0]=1;
    for(int i=1;i<maxn;i++)f[i]=f[i-1]*i%MOD;
}
int main()
{
    init();
    while(scanf("%d",&N)!=EOF)
    {
        for(int i=1;i<=N;i++)scanf("%d",&a[i]);
        memset(vis,0,sizeof(vis));
        LL ans=0;
        for(int i=1;i<=N;i++)
        {
            for(int j=1;j<i;j++)buf[j]=a[j];
            for(int j=1;j<=N;j++)
                if(!vis[j]&&j<a[i])
                {
                    int sum=0,tmp1=0;
                    buf[i]=j;
                    memset(vis1,0,sizeof(vis1));
                    for(int k=1;k<=i;k++)
                    {
                        for(int p=1;p<buf[k];p++)
                            if(!vis1[p])tmp1++;
                        vis1[buf[k]]=1;
                    }
                    ans+=(LL)tmp1*f[N- i]%MOD;
                    LL tmp=(N-i)*(N-i-1)*f[N-i]%MOD*250000002%MOD;
                    ans=(ans+tmp)%MOD;
                }
            vis[a[i]]=1;
        }
        cout<<ans%MOD<<endl;
    }
    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值