D e s c r i p t i o n Description Description
给定两个整数n; k,求1; 2; 3… n 的排列中,有k 对逆序对的排列的个数。
逆序对:假设为i,j,则是在排列中
i
<
j
i<j
i<j 和
a
[
i
]
>
a
[
j
]
a[i]>a[j]
a[i]>a[j]的一对数字
I n p u t Input Input
本题有多组数据,每行两个整数n; k。最后一行为"0 0" 表示结束。
O u t p u t Output Output
对每组数据,输出一行一个整数,表示答案。
S a m p l e Sample Sample I n p u t Input Input
3 0
3 1
3 2
3 3
4 2
4 10
13 23
18 80
0 0
S a m p l e Sample Sample O u t p u t Output Output
1
2
2
1
5
0
46936280
184348859235088
H i n t Hint Hint
1 <= 数据组数 <=100, 1 <= n <= 50, 0 <= k <= 1500。
T r a i n Train Train o f of of T h o u g h t Thought Thought
通过研究几组简单的数据我们可以发现
对于一个从1~n的排列而言,在第i个位置插入一个比n大的数字,就会和后面的n-i+1位数字全部形成逆序对,从而得出动态转移方程
k
=
0
k=0
k=0
t
o
to
to
m
i
n
(
i
−
1
,
j
)
min(i-1,j)
min(i−1,j)
f
[
i
]
[
j
]
f[i][j]
f[i][j] =
f
[
i
−
1
]
[
j
−
k
]
f[i − 1][j − k]
f[i−1][j−k]
又因为题目中的数据给的特别特别大
所以我们需要用到高精和压位等骚操作
C o d e Code Code
#include<cstdio>
#include<iostream>
using namespace std;
bool b; int n,m;
int f[55][1505][105];
void add(int x,int y,int z)
{
int tt=0;
for (int i=1; i<=10; ++i)
{
f[x][y][i]=f[x][y][i]+f[x-1][y-z][i]+tt;
tt=f[x][y][i]/1000000000;
f[x][y][i]%=1000000000;//高精和压位骚操作
}
}
int main()
{
freopen("inversion.in","r",stdin);
freopen("inversion.out","w",stdout);
f[1][0][1]=1;
f[2][0][1]=f[2][1][1]=f[3][0][1]=1;
f[3][1][1]=f[3][2][1]=2;
f[3][3][1]=1;//定初值
for (int i=4; i<=50; ++i)
for (int j=0; j<=1500; ++j)
{
b=false;
for (int k=0; k<=min(i-1,j); ++k)
if (f[i-1][j-k]!=0) b=true,add(i,j,k);//↑↑↑
}
scanf("%d%d",&n,&m);
while (n!=0 || m!=0)
{
int k=10;
while (k>1 && !f[n][m][k]) k--;
printf("%d",f[n][m][k]);
k--;
while (k)
{
if (f[n][m][k]<1e8) printf("0");
if (f[n][m][k]<1e7) printf("0");
if (f[n][m][k]<1e6) printf("0");
if (f[n][m][k]<1e5) printf("0");
if (f[n][m][k]<1e4) printf("0");
if (f[n][m][k]<1e3) printf("0");
if (f[n][m][k]<1e2) printf("0");
if (f[n][m][k]<1e1) printf("0");//压位的对应前导零
printf("%d",f[n][m][k--]);
}
printf("\n");
scanf("%d%d",&n,&m);
}
return 0;
}