[hdu5225][BC#40]Tom and permutation
好久没写题解了。。GDKOI被数位DP教做人了一发,现在终于来填数位DP的大坑了>_<。
发现自己以前写的关于数位DP的东西...因为没结合图形+语文水平拙计现在已经完全看不懂了嗯。
看来看去感觉还是这篇关于数位DP的介绍靠谱:http://wenku.baidu.com/view/d2414ffe04a1b0717fd5dda8.html
想题的时候结合图形食用效果更佳。
题意是说,对于一个给定的n的排列,要求出 (所有字典序<给定排列的排列)的逆序对个数和。
预处理出f[i]表示i的所有排列中,逆序对的个数。(事实上也是i个数的所有排列中逆序对的个数)
然后像那篇论文里面的姿势处理就好了。
每次把逆序对分为三种:已确定的数和后面的数之间的逆序对、已确定的数之间的逆序对,后面的数之间的逆序对。
前两种可以放到一起统计(和后面的数的具体顺序无关),第三种就是预处理出来的东西。
顺便在hdu上压了发代码最短= =(空间实在无力>_<
1 #include<cstdio> 2 #include<cstring> 3 #define ll long long 4 using namespace std; 5 const ll modd=1000000007; 6 ll f[101],a[101];//a数组存阶乘的值 7 ll i,j; 8 int n,x; 9 bool u[101]; 10 inline ll run(){ 11 ll ans=0,now=0,mn; 12 memset(u,0,n+1); 13 for(i=1;i<=n;i++){ 14 scanf("%d",&x);u[x]=1; 15 for(j=1,mn=0;j<x;j++) 16 if(!u[j])ans+=f[n-i]+a[n-i]*(now+mn),ans%=modd,mn++; 17 for(j=1;j<x;j++)if(!u[j])now++; 18 } 19 return ans; 20 } 21 int main(){ 22 a[1]=1;f[1]=0; 23 for(i=2;i<=100;i++){ 24 a[i]=a[i-1]*i%modd; 25 f[i]=(i*f[i-1]+a[i-1]*(i-1)*i/2)%modd; 26 } 27 while(scanf("%d",&n)==1)printf("%lld\n",run()); 28 return 0; 29 }