HDU5225 Tom and permutation(排列組合),hdu5225permutation
題意:
Tom學會了通過寫程式求出一個1-n的排列的逆序對數,但他的老師給了他一個難題:給出一個1-n的排列,求所有字典序比它小的1-n的排列的逆序對數之和。Tom一時不知道該怎麼做,所以他來找你幫他解決這個問題。因為數可能很大,答案對109+7模數。
從前往後推,先計算1-k的所有排列可以產生逆序總數,
先假設db[2]為1-2的結果,那麼我們來看3的排列,他是由1[2,3],2[1,3],3[1,2]三項組成,【】為所有排列的意思,第一項中【2,3】組合對結果貢獻db【2】,前面的1小於後面兩數,無貢獻,總共db【2】;第二項【1,3】組合對結果貢獻db【2】,前面2對後面的1始終產生1個逆序,總共db【2】+2!;第二項【1,2】組合對結果貢獻db【2】,前面的3對後面的1,2始終產生2個逆序,總共db【2】+2*2!;繼續推就能推出1-k的所有排列可以產生逆序總數。
然後看一個範例3 3 2 1,它的序列有
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
qq(i,j)計算第i位後面比j小的數字個數,
那麼前兩個為一組,產生2!*qq(1,1)+db【2】逆序,第三到第四產生2!*qq(1,2)+db【2】逆序,最後一組需要用類似數位的方法計算個數然後統計,代碼會比我說的清晰點。
代碼:
#pragma comment(linker, "/STACK:102400000,102400000")#include<iostream>#include<cstdio>#include<string>#include<cmath>#include<queue>#include<stack>#include<map>#include<cstring>#include<fstream>#include<algorithm>#define rep(i,a,b) for(long long i=(a);i<(b);i++)#define rev(i,a,b) for(int i=(a);i>=(b);i--)#define clr(a,x) memset(a,x,sizeof a)#define inf 0x3f3f3f3ftypedef long long LL;using namespace std;const double eps=10e-10;const LL mod=1000000007 ;const int maxn=105;const int maxm=600005;LL C(LL x){ if(x==0)return 0; LL ret=1; while(x>1) { ret=ret*x%mod; x--; } return ret;}LL da[105],db[105],sum[105],done[105];void inn(){ db[2]=1; rep(i,3,105) { db[i]=db[i-1]; rep(j,1,i) db[i]=(db[i]+j*C(i-1)+db[i-1])%mod; }}LL n;LL qq(LL a,LL b){ LL ans=0; rep(i,a+1,n) if(da[i]<b)ans++; return ans;}int main(){ inn(); while(~scanf("%I64d",&n)) { rep(i,0,n) scanf("%d",&da[i]); LL ans=0;clr(sum,0);clr(done,0); rep(i,0,n) { done[da[i]]=1; rep(j,1,da[i]) if(!done[j])sum[i]+=C(n-1-i); rep(j,1,da[i]) if(!done[j])ans=(ans+db[n-1-i]+C(n-1-i)*qq(i,j))%mod; } for(int i=n-2;i>=0;i--) { sum[i]+=sum[i+1]; ans=(ans+sum[i+1]*qq(i,da[i]))%mod; } printf("%I64d\n",ans); } return 0;}