【NOIP2015模拟11.5】俄罗斯套娃题解

Description

在这里插入图片描述

Input

在这里插入图片描述

Output

在这里插入图片描述

Sample Input

10 1000

Sample Output

3628800

Data Constraint

在这里插入图片描述

Solution

一道比较简单的dp。

对于60%的数据

f i , j f_{i,j} 表示插入 i i 这个数,逆序对个数为 j j 的方案数。
那么插入 i i 这个数所能产生的逆序对个数为 k k , k ϵ [ 0 , i 1 ) k\epsilon[0,i-1) 。很容易理解,因为你前面已经插入了1~i-1里的数,你插入在哪一个位置都行,比 i i 小的数最少有0个,做多有 i 1 i-1 个。

f i , j = k = 0 m i n ( j , i 1 ) f i 1 , j k f_{i,j}=\sum^{min(j,i-1)}_{k=0}f_{i-1,j-k}

对于100%的数据

到了这里还是不行,因为这道题的 n 3000 n\leq3000 ,而这个算法是 O ( n k 2 ) O(nk^2) 的,所以我们还要考虑优化。
经过观察,发现 k = 0 m i n ( j , i 1 ) f i 1 , j k \sum^{min(j,i-1)}_{k=0}f_{i-1,j-k} 这个东西可以用前缀和维护,用前缀和优化后就可以把时间复杂度优化至 O ( n k ) O(nk)

我比赛时忘记负数要加上模数了,爆成了70分,下次一定一定要注意。

还有一点要注意,记得要打滚动数组。

#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const ll mo=1e10+7; 
int n,m;
ll ans,f[3005],sum[3005],s[3005];
int main() {
	freopen("matryoshka.in","r",stdin);
	freopen("matryoshka.out","w",stdout);
	scanf("%d%d",&n,&m);
	f[0]=1;
	for(int i=0;i<=m;i++)sum[i]=1;
	for(int i=2;i<=n;i++) {
		s[0]=0;
		for(int j=0;j<=m;j++) {
			if(j-min(i-1,j)-1<0)f[j]=sum[j];
			else f[j]=(sum[j]-sum[j-min(i-1,j)-1]+mo)%mo;
			if(j>0)s[j]=(s[j-1]+f[j])%mo;
			else s[j]=f[j];
		}
		for(int j=0;j<=m;j++)sum[j]=s[j];
	}
	printf("%lld",(sum[m]+mo)%mo);
	fclose(stdin);
	fclose(stdout);
	return 0;	
}

猜你喜欢

转载自blog.csdn.net/MZHjr/article/details/107823487
今日推荐