【JZOJ】【高精】【DP】逆序对

D e s c r i p t i o n Description Description

给定两个整数n; k,求1; 2; 3… n 的排列中,有k 对逆序对的排列的个数。
逆序对:假设为i,j,则是在排列中 i &lt; j i&lt;j i<j a [ i ] &gt; a [ j ] a[i]&gt;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(i1,j)
f [ i ] [ j ] f[i][j] f[i][j] = f [ i − 1 ] [ j − k ] f[i − 1][j − k] f[i1][jk]
又因为题目中的数据给的特别特别大
所以我们需要用到高精和压位等骚操作

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值