LOJ - 6077 逆序对

时间:2021-08-09
本文章向大家介绍LOJ - 6077 逆序对,主要包括LOJ - 6077 逆序对使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题目

传送门

解法

\(\text{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} \]

\(\text{60 pts}\)

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

\(\text{100 pts}\)

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

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

的解的方案数。

可以钦定 \(m\)\(x_i\) 不满足性质,设这些 \(x_i\)\(i\) 之和为 \(s\)\(x_i\) 值为 \(i\) 恰好不满足性质)。那么方案数就是 \(\binom{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(n\sqrt 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,'\n');
	return 0;
}

原文地址:https://www.cnblogs.com/AWhiteWall/p/15120493.html