zoukankan      html  css  js  c++  java
  • 「Gym102978A」Ascending Matrix

    题目

    点这里看题目。

    分析

    非常巧妙的一道题目。

    首先,我们可以思考如果没有 (a_{R,C}=V) 的限制,问题应该如果求解。一种巧妙的思考方式是,我们可以对于 (iin [1,K)) 勾勒出 (le i) 的元素和 (>i) 的元素之间的分界线。这样的话,如果我们从 0 开始给行列的格点标号,那么每一条分界线一定是从 ((n,0))((0,m)) 的一条路径,并且路径上只会向上或者向右走。此外,对于任意两条分界线,它们之间互不跨越

    互不跨越如何处理?我们可以将其中一条路径平移,这样互不跨越就变成了互不相交。具体来说,原先的第 (i) 条线段,平移后的起点变成了 ((n-i+1,i-1)) ,终点变成了 ((m-i+1,i-1))。这样路径计数就变得易于处理了,我们可以直接套用 LGV 引理。

    灰色表示路径的重叠部分,虚线表示平移后的路径

    考虑处理 (a_{R,C}=V) 的限制。在平移前,这等价于 (P(R,C)) 的严格左下角恰好有 (V-1) 条路径经过。由于路径互不跨越,这就相当于(V-1) 经过了 (P) 的严格左下角且第 (V) 条路径经过了 (P) 的非严格右上角

    首先考虑第一条限制,我们可以直接将 (P) 按照第 (V-1) 条路径平移到 (P'),这样路径就被分成了“经过 (P') 严格左下角”和“没有经过 (P') 的严格左下角”两类,计算其中一种就可以推出另外一种,因此计算上无甚改变。而对于 (i<V-1),LGV 引理可以保证第 (i) 条路径不与第 (V-1) 条路径相交,结果就是第 (i) 条路径也会经过 (P') 的严格左下角。因此,对于第 (i) 条路径,我们也可以类似于第 (V-1) 条计算。

    再考虑第二条限制。注意到在平移之后,第 (V) 条路径只能经过 (P')严格右上角。这样一来,任何路径都不可能经过 (P'),所以我们建图的时候需要注意将经过 (P') 的方案去除。

    但是,这里的讨论是基于“第 (V-1) 条路径经过 (P) 的严格左下角和第 (V) 条路径经过 (P) 的非严格右上角”的前提的,实际 LGV 的时候我们还没法保证这一点(因为 LGV 引理的前提为:所有路径都是平等的)。因此,我们需要引入形式变元 (x) 来刻画路径条数。这样,行列式的元素和结果都变成了多项式。考虑到多项式本身运算不便,我们可以计算对多项式代值后的结果,再插值得到原多项式。

    这样,我们就得到了 (O(k^4)) 的算法。

    小结:

    1. 一开始对于问题的转化很巧妙。对于具有一定单调性的对象,我们都可以考虑勾勒出它的断点,并且研究断点

      利用平移的方法,我们将路径的互不跨越变成了互不相交

    2. 仍然是利用单调性,将原先对于 (V-1) 条路径的讨论简化到了对于两条;

    3. 注意细节,不能忽视(V) 条路径经过 (P) 的非严格右上角这一条件。

    代码

    #include <cstdio>
    #include <utility>
    #include <iostream>
    
    #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;
    const int MAXK = 105, MAXN = 1005;
    
    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 binom[MAXN][MAXN];
    
    int val[MAXK], dp[MAXK], tmp[MAXK];
    
    int coe[MAXK][MAXK][2];
    int A[MAXK][MAXK];
    
    int N, M, K, R, C, V;
    
    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 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 )/*{{{*/
    {
    	rep( i, 0, n )
    	{
    		binom[i][0] = binom[i][i] = 1;
    		rep( j, 1, i - 1 ) binom[i][j] = Add( binom[i - 1][j], binom[i - 1][j - 1] );
    	}
    }/*}}}*/
    
    int Gauss( const int n )/*{{{*/
    {
    	int ret = 1, idx, tmp, inv;
    	for( int i = 1 ; i <= n ; i ++ )
    	{
    		idx = -1;
    		for( int j = i ; j <= n ; j ++ )
    			if( A[j][i] ) { idx = j; break; }
    		if( idx == -1 ) return 0;
    		if( idx ^ i ) 
    			std :: swap( A[i], A[idx] ), ret = mod - ret;
    		inv = Inv( A[i][i] );
    		for( int j = i + 1 ; j <= n ; j ++ )
    			if( ( tmp = Mul( inv, A[j][i] ) ) )
    				for( int k = 1 ; k <= n ; k ++ )
    					A[j][k] = Sub( A[j][k], Mul( tmp, A[i][k] ) );
    	}
    	rep( i, 1, n ) ret = Mul( ret, A[i][i] );
    	return ret;
    }/*}}}*/
    
    std :: pair<int, int> Calc( const int n, const int m, const int r, const int c )/*{{{*/
    {
    	int fir = 0, sec = 0;
    	if( r <= 0 || c <= 0 ) sec = 0;
    	else if( r > n || c > m ) sec = binom[n + m][m];
    	else
    	{
    		if( r <= c )
    			rep( k, 0, r - 1 )
    				sec = Add( sec, Mul( binom[n + c - r][n - k], binom[m - c + r][k] ) );
    		else
    			rep( k, 0, c - 1 )
    				sec = Add( sec, Mul( binom[n - r + c][k], binom[m + r - c][m - k] ) );
    	}
    	fir = binom[n + m][m];
    	if( 0 <= r && 0 <= c && r <= n && c <= m )
    		fir = Sub( fir, Mul( binom[n - r + c][c], binom[m - c + r][r] ) );
    	return std :: make_pair( Sub( fir, sec ), sec );
    }/*}}}*/
    
    int main()
    {
    	read( N ), read( M ), read( K );
    	read( R ), read( C ), read( V );
    	if( K == 1 ) return puts( "1" ), 0;
    	Init( N + M ), R -= K - V, C -= K - V;
    	rep( i, 1, K - 1 ) rep( j, 1, K - 1 )
    	{
    		int n = N + j - i, m = M + i - j;
    		if( n < 0 || m < 0 ) continue;
    		std :: pair<int, int> tmp = Calc( n, m, R - N + i - 1 + n, C + i - 1 );
    		coe[i][j][0] = tmp.first, coe[i][j][1] = tmp.second;
    	}
    	rep( v, 1, K )
    	{
    		rep( i, 1, K - 1 ) rep( j, 1, K - 1 )
    			A[i][j] = Add( coe[i][j][0], Mul( v, coe[i][j][1] ) );
    		val[v] = Gauss( K - 1 );
    	}
    	dp[0] = 1;
    	rep( i, 1, K )
    		per( j, K, 0 )
    		{
    			dp[j] = Mul( dp[j], mod - i );
    			if( j ) dp[j] = Add( dp[j], dp[j - 1] );
    		}
    	int ans = 0;
    	rep( i, 1, K )
    	{
    		rep( j, 0, K ) tmp[j] = dp[j];
    		rep( j, 0, K )
    		{
    			if( j ) tmp[j] = Sub( tmp[j], tmp[j - 1] );
    			tmp[j] = Mul( tmp[j], Inv( mod - i ) );
    		}
    		int inv = val[i];
    		rep( j, 1, K ) if( i != j )
    			inv = Mul( inv, Inv( Sub( i, j ) ) );
    		ans = Add( ans, Mul( inv, tmp[V - 1] ) );
    	}
    	write( ans ), putchar( '
    ' );
    	return 0;
    }
    
  • 相关阅读:
    HSTS详解
    cdnbest 节点和主控连接不上原因主要查看几点
    正则表达式30分钟入门教程
    cdn贝四层协议配置端口映射TCP端口转发
    关于HSTS安全协议的全面详细解析
    cdnbest配置强制ssl跳转
    THUWC2020游记
    2019 CSP-S Day2-T1 Emiya 家今天的饭(DP)
    网络流之费用流(最小费用最大流) 学习笔记
    网络流之最大流 学习笔记
  • 原文地址:https://www.cnblogs.com/crashed/p/15230108.html
Copyright © 2011-2022 走看看