HDU 5225 Tom and permutation

对于连续的排列直接的逆序 打表或者首先就可以看出规律

设f(n)表示n个数时 每个排列所对应的逆序数

显然有 f(1)=1, f(2)=0,1 , f(3)=0,1,1,2,2,3 , f(4)= 0,1,1,2,2,3,   1,2,2,3,3,4 , 2,3,3,4,4,5 , 3,4,4,5,5,6

即为f(n-1)的所有的数为一组 扩展成n组 第一组为f(n-1) 后面每组比前一组中所有的数大1

所以我们可以预先把dp[i](表示f(i)的总和)用cnt[i](表示f(i)的个数 即i的阶乘)递推的处理出来 

而对于3,1,4,5,2 这样的一组数时

首先对于3这位 要加上以1 和 2开头时f(n)的值 即 加上2*dp[4]+1*cnt[4] (4为后面剩余的个数,2为比3小的个数 ,1为(比3小的个数-1)的前缀和)

而考虑下一位时 这时已经假定3已经选定 (第一位为1和2的已经加完了) 所以3是对后面有影响的 影响为2(比3小的个数) 而当我们考虑第3位的数字4的时候 因为3已经固定 

所以4要相当于3来处理 所以我们可以用一个树状数组来维护 

个人感觉这个dp我想麻烦了 没有想到很优美的递推方法 但这个思路还是蛮好理解的(就是求它之前的所以排列的逆序数)  不足就是要维护和记录好多东西(>﹏<)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cctype>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<algorithm>
#include<set>
#define scnaf scanf
#define cahr char
#define bug puts("bugbugbug");
using namespace std;
typedef long long ll;
const int mod=1000000007;
const int maxn=1e5+5;
const int inf=1e9;
int N;
const int NV=105;
int c[NV];
inline int lowbit(int t)
{
    return t&(-t);
}
void update(int x,int v)
{
    while(x<=N)
    {
        c[x]+=v;
        x+=lowbit(x);
    }
}
int query(int x)
{
    int ans=0;
    while(x>0)
    {
        ans+=c[x];
        x-=lowbit(x);
    }
    return ans;
}
int findkth(int k)
{
    int idx = 0;
    for(int i=20; i>=0; i--)
    {
        idx |= 1 << i;
        if(idx <= N && c[idx] < k)
            k -= c[idx];
        else idx ^= 1 << i;
    }
    return idx + 1;
}
   ll dp[105],cnt[105];
void init()
{
   dp[1]=0;
   cnt[1]=1;
   for(int i=2;i<=100;i++)
   {
       dp[i]=dp[i-1]*i%mod;
       dp[i]=(dp[i]+cnt[i-1]*(i*(i-1)/2)%mod)%mod;
       cnt[i]=cnt[i-1]*i%mod;
   }
}
int a[105];
int main()
{
    init();
       int n;
    while(~scanf("%d",&n))
    {
        N=n;
       for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
        ll ans=0;
        memset(c,0,sizeof(c));
        for(int i=1;i<=n;i++) update(i,1);
        ll fuck=0;
        for(int i=1;i<=n;i++)
        {
            int num=query(a[i]);
            ans += dp[n-i] * (num-1) % mod + cnt[n-i] *((num-1)*(num-2)/2)%mod;
            ans%=mod;
            ans += fuck * (num-1) % mod * cnt[n-i] %mod;
            ans%=mod;
            fuck=fuck+num-1;
            update(a[i],-1);
        }
        memset(c,0,sizeof(c));
        printf("%lld\n",ans);
    }
}


 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值