1. 程式人生 > >洛谷P1521 求逆序對 題解

洛谷P1521 求逆序對 題解

-i can 由於 逆序 family std sum 16px div

題意:

  求1到n的全排列中有m對逆序對的方案數。

思路:

  1.f[i][j]表示1到i的全排列中有j對逆序對的方案數。
  2.顯然,1到i的全排列最多有(i-1)*i/2對逆序對,而對於f[i][j]來說,新加入一個數i+1,產生的新的逆序對數與插入的位置有關(數目為插入的數的位置之後的數的數目),於是n^4暴力就新鮮出爐了。
  3.換一個角度來說,當i>j的時候,我們枚舉i的全排列的第一位的數字,如果是1,那麽就要求剩下i-1個數中有j個全排列,如果是2,要求剩下i-1個數中有i-2個
全排列,依次類推,得到了第一個方程:f[i][j]=sum{f[i-1][0],f[i-1][1],f[i-1][2].....f[i-1][j]}
    當i<=j的時候,由於i的全排列中最大的數字是i,所以把i放到第一位上,由第一位最多能能產生i-1個逆序對,把1放到第一位上能產生0個逆序對,所以i-1-1+1=i-1,這時的f[i][j]就要由f[i-1][j]開始算上他自己,總共i-1項的和。因此:f[i][j]=sum{f[i-1][j],f[i-1][j-1],f[i-1][j-2].....f[i-1][j-i+1]}

反思:

  我只會暴力,優化只能理性愉悅一下了,好菜啊……

代碼:

  n^4暴力:

 1 #include<cstdio>
 2 int n,m,i,j,k,f[201][10000];
 3 
 4 int main()
 5 {
 6     scanf("%d%d",&n,&m);
 7     f[1][0]=1;
 8     for (i=1;i<n;++i)
 9         for (j=0;j<=(i-1)*i>>1;++j)
10             for (k=0;k<=i;++k)
11                 if
((f[i+1][j+i-k]+=f[i][j])>=10000) f[i+1][j+i-k]-=10000; 12 printf("%d\n",f[n][m]); 13 return 0; 14 }

  n^3:

 1 #include<cstdio>
 2 int n,m,i,j,f[101][5000];
 3 
 4 int main()
 5 {
 6     scanf("%d%d",&n,&m);
 7     f[1][0]=1;
 8     for (i=2;i<=n;++i)
 9     {
10         for (j=0;j<i;++j) f[i][j]=(f[i][j-1
]+f[i-1][j])%10000;//因為f[i,j]裏面統計的是類似於前綴和的一個東西,f[i][j]只比f[i][j-1]多了f[i-1][j]這一項 11 for (;j<=(i-1)*i>>2;++j) f[i][j]=(f[i][j-1]+f[i-1][j]-f[i-1][j-i])%10000;//這時求和的項數確定了,就是i-1項,於是f[i,j]比f[i][j-1]多了f[i-1][j]這一項,少了f[i-1][j-i]這一項 12 for (;j<=(i-1)*i>>1;++j) f[i][j]=f[i][((i-1)*i>>1)-j];//因為數對總數為i*(i-1)/2,而一個數對要麽是逆序對要麽是順序對,因此f[i][j]是關於f[i][i*(i-1)/4]呈現中心對稱的 13 } 14 printf("%d\n",(f[n][m]+10000)%10000); 15 return 0; 16 }

洛谷P1521 求逆序對 題解