zoukankan      html  css  js  c++  java
  • 「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;
    }
    
  • 相关阅读:
    第二季-专题11-世界一下变大了-MMU
    第二季-专题10-C语言环境初始化
    第二季-专题9--代码搬移不可少
    第二季-专题8-不用内存怎么行
    第二季-专题6-点亮指路灯
    第二季-专题7-ARM跑快了---时钟初始化
    第二季-专题5-核心初始化
    第二季-专题4-我是bootloader设计师
    消除苹果系统对边框的优化
    css3
  • 原文地址:https://www.cnblogs.com/crashed/p/15118733.html
Copyright © 2011-2022 走看看