「2017 山东一轮集训 Day7」逆序对

题目

给定 (n,k),求所有 ({1,2,dots,n}) 的排列中,逆序对数量为 (k)​ 的排列的数量,对 (10^9+7) 取模。

数据范围:对于 (100\%) 的数据,满足 (1le nle 10^5,1le kle min{10^5,inom{n}{2}})

分析

考虑逆序对数的一种描述方式:

(x_i)(i)​ 这个元素作为较前者时贡献的逆序对数量,那么总逆序对数量即 (sum_i x_i)

显然有 (0le x_i<i)。更重要的是,所有的排列与所有的 (x) 组合是双射关系,我们只需要对 (x) 计数即可。

(x) 确定的时候,我们从小到大加入每一个元素,那么 (x_i) 即可唯一确定元素 (i)​ 在加入时的位置。

处理这种问题的自然想法是容斥,考虑枚举超出上界的元素集合,从而得到:

[sum_{Ssubseteq{1,2,dots,n}}(-1)^{|S|}inom{k-sum_{xin S}x+n-1}{n-1} ]

方法 1

注意到后面的组合数仅和 (sum_{xin S}x) 相关,我们自然可以想到计算容斥系数。

(f_i)(sum_{xin S}x=i)​ 时的容斥系数,容易写出 (f) 的生成函数:

[F(x)=sum_{i=0}^kf_i=prod_{i=1}^n(1-x^i) ]

如何计算 (F(x)mod x^{k+1})?由于乘积式的每一项形式简单,我们可以先 (ln)(exp)

[egin{aligned} F(x) &=prod_{i=1}^n(1-x^i)\ &=exp(sum_{i=1}^nln(1-x^i))\ &=exp(-sum_{i=1}^nsum_{jge 1}frac{x^{ji}}{j}) end{aligned} ]

(exp) 内部的多项式易于计算。但由于模数是 (10^9+7)​,所以需要写任意模数的多项式运算。

此处使用半在线卷积计算 (exp)​ 可能会得到更小的常数。

方法 2

直接将 (|S|)(sum_{xin S}x) 都存入状态,我们可以设计 DP:

(g_{i,j})(|S|=i,sum_{xin S}x=j)(S) 数量。有一点难处理的地方在于,我们需要保证 (S) 内部的元素不同。

在不计顺序的情况下,(S)​​ 与某个上升序列对应。那么每个上升序列可以被如此构造:首先,序列为空;此后每一步,向序列中的每个元素加上若干个 1,并在序列头部插入一个 1;在你想要的时候停止操作即得到了一个上升序列。

中间的这一步也相当于要么向每个元素 +1,要么 +1 后在头部插入 1

另外还要注意,我们需要保证 (S) 的元素不会超过 (n)​,然而我们的构造方法并不能避免这种情况——手动规避即可:由于序列每次操作只会 +1,所以最多会出现最后一个元素为 (n+1) 的情况,减去即可。

看似复杂度还是 (O(nk)),但其实不然;由于 (S) 中的元素互不相同,因此 (jge frac{i(i+1)}{2}),即对于 (j),有效的 (i)(O(sqrt{n})) 个。

(n,k) 同阶的时候时空复杂度为 (O(nsqrt{n}))

小结:

  1. 方法 1 中对乘积先取 (ln) 再取 (exp) 的方式,对于多项简单乘积很有效;
  2. 方法 2 中注意这样差分思想的单调序列构造方式。此外同样要准确分析状态数量,一定要注意“不同元素和较小”的性质!!!

代码

方法 1

偷懒了,模数是 (998244353)

#include <cstdio>
#include <algorithm>

#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )

const int mod = 998244353, g = 3, phi = mod - 1;
const int MAXN = 4e5 + 5;

template<typename _T>
void read( _T &x )/*{{{*/
{
	x = 0; char s = getchar(); int f = 1;
	while( ! ( '0' <= s && s <= '9' ) ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
	while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
	x *= f;
}/*}}}*/

template<typename _T>
void write( _T x )/*{{{*/
{
	if( x < 0 ) putchar( '-' ), x = -x;
	if( 9 < x ) write( x / 10 );
	putchar( x % 10 + '0' );
}/*}}}*/

int fac[MAXN], ifac[MAXN];

int N, K;

inline int Qkpow( int, int );
inline int Mul( int x, int v ) { return 1ll * x * v % mod; }
inline int Inv( const int a ) { return Qkpow( a, mod - 2 ); }
inline int Sub( int x, int v ) { return ( x -= v ) < 0 ? x + mod : x; }
inline int Add( int x, int v ) { return ( x += v ) >= mod ? x - mod : x; }
inline int C( int n, int m ) { return n < m ? 0 : Mul( fac[n], Mul( ifac[m], ifac[n - m] ) ); }

inline int Qkpow( int base, int indx )/*{{{*/
{
	int ret = 1;
	while( indx )
	{
		if( indx & 1 ) ret = Mul( ret, base );
		base = Mul( base, base ), indx >>= 1;
	}
	return ret;
}/*}}}*/

inline void Init( const int n = 3e5 )/*{{{*/
{
	fac[0] = 1; rep( i, 1, n ) fac[i] = Mul( fac[i - 1], i );
	ifac[n] = Inv( fac[n] ); per( i, n - 1, 0 ) ifac[i] = Mul( ifac[i + 1], i + 1 );
}/*}}}*/

namespace Basic/*{{{*/
{
	const int L = 18;

	int w[MAXN];

	void NTTInit( const int n = 1 << L )/*{{{*/
	{
		w[0] = 1, w[1] = Qkpow( g, phi >> L ); rep( i, 2, n - 1 ) w[i] = Mul( w[i - 1], w[1] );
	}/*}}}*/

	void DIF( int *coe, const int n )/*{{{*/
	{
		int *wp, p, e, o;
		for( int s = n >> 1 ; s ; s >>= 1 )
			for( int i = 0 ; i < n ; i += s << 1 )
			{
				wp = w, p = ( 1 << L ) / ( s << 1 );
				for( int j = 0 ; j < s ; j ++, wp += p )
				{
					e = coe[i + j], o = coe[i + j + s];
					coe[i + j] = Add( e, o );
					coe[i + j + s] = Mul( *wp, Sub( e, o ) );
				}
			}
	}/*}}}*/

	void DIT( int *coe, const int n )/*{{{*/
	{
		int *wp, p, k;
		for( int s = 1 ; s < n ; s <<= 1 )
			for( int i = 0 ; i < n ; i += s << 1 )
			{
				wp = w, p = ( 1 << L ) / ( s << 1 );
				for( int j = 0 ; j < s ; j ++, wp += p )
					k = Mul( *wp, coe[i + j + s] ),
					coe[i + j + s] = Sub( coe[i + j], k ),
					coe[i + j] = Add( coe[i + j], k );
			}
		std :: reverse( coe + 1, coe + n );
		int inv = mod - ( mod - 1 ) / n; rep( i, 0, n - 1 ) coe[i] = Mul( coe[i], inv );
	}/*}}}*/
}/*}}}*/

namespace Solution
{	
	int P[MAXN], Q[MAXN];
	int f[MAXN], g[MAXN];
	int inv[MAXN];

	void Divide( const int l, const int r )
	{
		if( l == r )
		{
			if( l == 0 ) g[l] = 1;
			else g[l] = Mul( g[l], inv[l] );
			return ;
		}
		int mid = l + r >> 1; Divide( l, mid );
		int L = 1; for( ; L < r - l + 1 + mid - l + 1 ; L <<= 1 );
		rep( i, 0, L - 1 ) P[i] = Q[i] = 0;
		rep( i, l, mid ) P[i - l] = g[i];
		rep( i, 0, r - l ) Q[i] = f[i];
		Basic :: DIF( P, L ), Basic :: DIF( Q, L );
		rep( i, 0, L - 1 ) P[i] = Mul( P[i], Q[i] );
		Basic :: DIT( P, L );
		rep( i, mid + 1, r ) g[i] = Add( g[i], P[i - l] );
		Divide( mid + 1, r );
	}

	void Solve()
	{
		rep( i, 1, K ) inv[i] = Inv( i );
		rep( j, 1, N )
			for( int k = 1 ; 1ll * k * j <= K ; k ++ )
				f[j * k] = Sub( f[j * k], inv[k] );
		rep( i, 0, K ) f[i] = Mul( i, f[i] );
		Basic :: NTTInit();
		Divide( 0, K );
		int ans = 0;
		rep( i, 0, K )
			ans = Add( ans, Mul( g[i], C( K - i + N - 1, N - 1 ) ) );
		write( ans ), putchar( '
' );
	}
}

int main()
{
	read( N ), read( K ), Init();
	Solution :: Solve();
	return 0;
}

方法 2

#include <cstdio>

#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )

const int mod = 1e9 + 7;
const int MAXN = 2e5 + 5, MAXB = 700;

template<typename _T>
void read( _T &x )/*{{{*/
{
	x = 0; char s = getchar(); int f = 1;
	while( ! ( '0' <= s && s <= '9' ) ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
	while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
	x *= f;
}/*}}}*/

template<typename _T>
void write( _T x )/*{{{*/
{
	if( x < 0 ) putchar( '-' ), x = -x;
	if( 9 < x ) write( x / 10 );
	putchar( x % 10 + '0' );
}/*}}}*/

int dp[MAXB][MAXN];

int fac[MAXN], ifac[MAXN];
int N, K;

inline int Qkpow( int, int );
inline int Mul( int x, int v ) { return 1ll * x * v % mod; }
inline int Inv( const int a ) { return Qkpow( a, mod - 2 ); }
inline int Sub( int x, int v ) { return ( x -= v ) < 0 ? x + mod : x; }
inline int Add( int x, int v ) { return ( x += v ) >= mod ? x - mod : x; }
inline int C( int n, int m ) { return n < m ? 0 : Mul( fac[n], Mul( ifac[m], ifac[n - m] ) ); }
inline void Upt( int &x, const int v ) { x = Add( x, v ); }

inline int Qkpow( int base, int indx )/*{{{*/
{
	int ret = 1;
	while( indx )
	{
		if( indx & 1 ) ret = Mul( ret, base );
		base = Mul( base, base ), indx >>= 1;
	}
	return ret;
}/*}}}*/

void Init( const int n = 2e5 )/*{{{*/
{
	fac[0] = 1; rep( i, 1, n ) fac[i] = Mul( fac[i - 1], i );
	ifac[n] = Inv( fac[n] ); per( i, n - 1, 0 ) ifac[i] = Mul( ifac[i + 1], i + 1 );
}/*}}}*/

int main()
{
	read( N ), read( K ), Init();
	dp[0][0] = 1; 
	int ans = C( K + N - 1, N - 1 ), res;
	for( int i = 1 ; ; i ++ )
	{
		if( 1ll * i * ( i + 1 ) / 2 > K ) break;
		for( int j = i * ( i + 1 ) >> 1 ; j <= K ; j ++ )
		{
			if( j >= i ) Upt( dp[i][j], Add( dp[i][j - i], dp[i - 1][j - i] ) );
			if( j > N ) Upt( dp[i][j], mod - dp[i - 1][j - N - 1] );
			res = Mul( dp[i][j], C( K - j + N - 1, N - 1 ) );
			Upt( ans, i & 1 ? mod - res : res );
		}
	}
	write( ans ), putchar( '
' );
	return 0;
}
原文地址:https://www.cnblogs.com/crashed/p/15118733.html