俄罗斯套娃

description

求出满足逆序对个数不大于\(k\)\(n\)的排列个数.

solution

挺水的一道签到题,但忘取模20pts滚粗\(\cdots\)

首先我们定义\(f[i][j]\)表示\(i\)的排列中逆序对个数为\(j-1\)的数量,根据打表找规律可知,\(f[i][j]=f[i-1][j]+f[i][j-1],j<=i\);\(f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-\frac{(i-1)\cdot (i-2)}{2}],j>i\),预处理出\(f[1][1]=1\),于是乎,\(\Omicron(n\cdot k)\)复杂度算法就此诞生.另外,为了满足空间限制,我们需要开滚动数组滚掉第一维.

code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#include<set>
#define R register
#define next kdjadskfj
#define debug puts("mlg")
#define mod 10000000007
#define Mod(x) ((x%mod+mod)%mod)
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
inline ll read();
inline void write(ll x);
inline void writeln(ll x);
inline void writesp(ll x);
ll n,k; 
ll f[2][3100];
ll res=0;
int main(){	
	freopen("matryoshka.in","r",stdin);
	freopen("matryoshka.out","w",stdout);
	n=read();k=read();
	f[1][1]=1;
	for(R ll i=2;i<=n;i++){
		ll r=min(k+1,((i*(i-1))>>1)+1);
		for(R ll j=1;j<=r;j++){
			if(j<=i) f[i&1][j]=Mod(f[(i&1)^1][j]+f[i&1][j-1]);
			else f[i&1][j]=Mod(f[i&1][j-1]+f[(i&1)^1][j]-f[(i&1)^1][j-i]);
		}
	}
	for(R ll i=1;i<=min(k+1,(((n*(n-1))>>1)+1));i++) res=Mod(res+f[n&1][i]);
	writeln(res);
}
inline ll read(){ll x=0,t=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') t=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*t;}
inline void write(ll x){if(x<0){putchar('-');x=-x;}if(x<=9){putchar(x+'0');return;}write(x/10);putchar(x%10+'0');}
inline void writesp(ll x){write(x);putchar(' ');}
inline void writeln(ll x){write(x);putchar('\n');}
posted @ 2020-08-05 17:07  月落乌啼算钱  阅读(100)  评论(0编辑  收藏  举报