1. 程式人生 > 其它 >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;
}