LOJ

题目

传送门

解法

( ext{40 pts})

从小到大放置 (n) 个数,对于第 (i) 个数就有 (i) 个位置放置,贡献值域为 ([0,i-1])

(dp_{i,j}) 为前 (i) 个数形成 (j) 个逆序对的方案数。就有:

[dp_{i,j}=sum_{k=0}^{i-1} dp_{i-1,j-k} ]

( ext{60 pts})

[dp_{i,j}-dp_{i,j-1}=dp_{i-1,j}-dp_{i-1,j-i} ]

( ext{100 pts})

问题可以转换成这样的形式:求满足

[sum_{i=1}^n x_i=k,x_ile i-1 ]

的解的方案数。

可以钦定 (m)(x_i) 不满足性质,设这些 (x_i)(i) 之和为 (s)(x_i) 值为 (i) 恰好不满足性质)。那么方案数就是 (inom{n-1}{k+n-1-s}),容斥系数是 ((-1)^m)

这显然过不去,但其实我们并不关心是哪些 (x_i) 超过限制,我们关心的是 (s)(m)

那么令 (dp_{i,j}) 为选 (i) 个数,它们的和为 (j) 的方案数。由于这 (i) 个数 互异,可以这样转移:

  • 将所选数整体 (+1)(dp_{i,j}=dp_{i,j-i})
  • 将所选数整体 (+1),再加入一个值为 (1) 的数。(dp_{i,j}=dp_{i,j}+dp_{i-1,j-i})
  • 有数字加出了 (n),需要减去。(dp_{i,j}=dp_{i,j}-dp_{i-1,j-(n+1)})

现在就是 (mathcal O(n^2)) 的咯?实际上,由于 (k) 的限制,我们并不需要选这么多数。考虑最节省的情况就是连续的等差数列,设项数为 (x)

[frac{(1+x)cdot x}{2}=k ]

这样 (x) 大概是 (sqrt k) 级别的。总复杂度 (mathcal O(nsqrt n))

代码

#include <cstdio>
#define print(x,y) write(x),putchar(y)

template <class T>
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while((s=getchar())>'9' or s<'0')
		f|=(s=='-');
	while(s>='0' and s<='9')
		x=(x<<1)+(x<<3)+(s^48),
		s=getchar();
	return f?-x:x;
}

template <class T>
inline void write(const T x) {
	if(x<0) {
		putchar('-'),write(-x);
		return;
	}
	if(x>9) write(x/10);
	putchar(x%10^48);
}

#include <cmath>

const int mod=1e9+7,maxn=1e5+5;

int n,k,fac[maxn<<1],ifac[maxn<<1];
int dp[600][maxn];

int qkpow(int x,int y) {
	int r=1;
	while(y) {
		if(y&1) r=1ll*r*x%mod;
		x=1ll*x*x%mod; y>>=1;
	}
	return r;
}

void init() {
	fac[0]=1;
	for(int i=1;i<=n+k;++i)
		fac[i]=1ll*fac[i-1]*i%mod;
	ifac[n+k]=qkpow(fac[n+k],mod-2);
	for(int i=n+k-1;i>=0;--i)
		ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
}

int C(int n,int m) {
	if(n<m or n<0 or m<0)
		return 0;
	return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}

int main() {
	n=read(9),k=read(9); init();
	int m=sqrt(k*2)+5;
	dp[0][0]=1;
	for(int i=1;i<=m;++i)
		for(int j=i;j<=k;++j) {
			dp[i][j]=(dp[i][j-i]+dp[i-1][j-i])%mod;
			if(j>=n+1)
				dp[i][j]=(dp[i][j]-dp[i-1][j-(n+1)]+mod)%mod;
		}
	int ans=C(n+k-1,n-1);
	for(int i=1;i<=k;++i) {
		int tmp=0;
		for(int j=1;j<=m and j<=n;++j)
			if(j&1) tmp=(tmp-dp[j][i]+mod)%mod;
			else tmp=(tmp+dp[j][i])%mod;
		ans=(ans+1ll*C(k+n-1-i,n-1)*tmp%mod)%mod;
	}
	print(ans,'
');
	return 0;
}
原文地址:https://www.cnblogs.com/AWhiteWall/p/15120493.html