zoukankan      html  css  js  c++  java
  • [AGC019E]Shuffle and Swap

    题目

    点这里看题目。

    分析

    题目明显是要求我们求方案数。

    显然这道题没有办法直接做。

    考虑转化一下题目条件。可以发现我们应该让 (A) 中多余的 1 换到 (A) 中缺少 1 的位置去。为了使描述更加清晰,我们这样定义:

    1. 公共点((P)):满足 (A_i=1land B_i=1)(i)。可以发现无论怎么交换,最终 (P) 上总是 1 。

    2. 起点((S)):满足 (A_i=0land B_i=1)(i)。我们需要将 (S) 上的 0 转移走。

    3. 终点((E)):满足 (A_i=1land B_i=0)(i)。我们需要将 (S) 上的 0 转移到 (E) 上来。

    可以发现,最终可以使得 (A=B) 的操作序列必然满足:

    连接边 ((a_i,b_i)),则图的形态应该是一大堆(S)(E) 作为端点,(P) 作为中间点的链

    注意这里的链应该是“有向”的,即我们不能倒着操作一条链。

    好的,这样已经清晰多了。我们考虑写出状态和转移:

    (f(i,j)):使用了 (i)(P) ,组成了 (j) 条链的真实序列方案数。

    不难考虑转移:

    1. 加入一个新的 (P) 。首先我们应该选取它所在的链((j)),钦定它在末尾,再考虑它的标号((i))。此时的贡献就是 (f(i-1,j) imes i imes j)

    2. 加入一个新的链。我们继续钦定它放在末尾,并且考虑 (S)(E) 的标号((j^2))。此时的贡献就是 (f(i,j-1) imes j^2)

    需要注意的是,每次转移必然会导致真实序列(也就是 (a)(b))长度加一。我们同样钦定每次新增后放在末尾

    真实情况下,一条链可能会有许多种对应的真实序列,而同一条链的不同的真实序列是由不同转移顺序来区分的。

    于是就有转移:

    [f(i,j)=i imes j imes f(i-1,j)+j^2 imes f(i,j-1) ]


    考虑统计答案。注意我们不一定要所有的 (P) 都在 (S-E) 链上。因此我们需要枚举一下不在链上的 (P) 的数量。

    (P) 点有 (s) 个,(S)(E) 各有 (t) 个。

    因此有答案为:

    [sum_{i=0}^sinom{s}{i} imes (i!)^2 imes f(s-i,t) imes inom{s+t}{i} ]

    其中 (inom{s}{i} imes (i!)^2) 是在计算不在链上的 (P)带标号形态(inom{s+t}{i}) 是在合并两个序列。

    最后我们就得到了时间为 (O(n^2)) 的算法。

    本题一些有价值的点:

    1. 考虑序列的相同与不同,于是就有了 (P,S,E) 三种点。

    2. 将交换看成边,发现链的性质。同时这也令人想起 树上的数

    3. 考虑序列的 DP 的时候,要么考虑标号,要么考虑位置,同时考虑会算重。

    代码

    AC 记录

    #include <cstdio>
    
    const int mod = 998244353;
    const int MAXN = 10005;
    
    template<typename _T>
    void read( _T &x )
    {
    	x = 0;char s = getchar();int f = 1;
    	while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
    	while( s >= '0' && 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 ) + 1; }
    	if( 9 < x ){ write( x / 10 ); }
    	putchar( x % 10 + '0' );
    }
    
    int f[MAXN][MAXN];
    int fac[MAXN], ifac[MAXN];
    char A[MAXN], B[MAXN];
    int N;
    
    int qkpow( int base, int indx )
    {
    	int ret = 1;
    	while( indx )
    	{
    		if( indx & 1 ) ret = 1ll * ret * base % mod;
    		base = 1ll * base * base % mod, indx >>= 1;
    	}
    	return ret;
    }
    
    void init( const int siz )
    {
    	fac[0] = 1;
    	for( int i = 1 ; i <= siz ; i ++ ) fac[i] = 1ll * fac[i - 1] * i % mod;
    	ifac[siz] = qkpow( fac[siz], mod - 2 );
    	for( int i = siz - 1 ; ~ i ; i -- ) ifac[i] = 1ll * ifac[i + 1] * ( i + 1 ) % mod;
    }
    
    int C( const int n, const int m ) 
    { 
    	if( n < m || n < 0 || m < 0 ) return 0;
    	return 1ll * fac[n] * ifac[m] % mod * ifac[n - m] % mod;
    }
    
    void add( int &x, const int v ) { x = ( x + v >= mod ? x + v - mod : x + v ); }
    
    int main()
    {
    	int S = 0, T = 0;
    	scanf( "%s%s", A + 1, B + 1 );
    	for( N = 1 ; A[N] ; N ++ )
    	{
    		int a = A[N] - '0', b = B[N] - '0';
    		if( a && b ) S ++;
    		if( a && ! b ) T ++;
    	}
    	init( N );
    	f[0][0] = 1;
    	for( int i = 0 ; i <= S ; i ++ )
    		for( int j = 0 ; j <= T ; j ++ )
    		{
    			if( i ) add( f[i][j], 1ll * f[i - 1][j] * i % mod * j % mod );
    			if( j ) add( f[i][j], 1ll * f[i][j - 1] * j % mod * j % mod );
    		}
    	int ans = 0;
    	for( int i = 0 ; i <= S ; i ++ )
    		add( ans, 1ll * C( S + T, i ) * C( S, i ) % mod * fac[i] % mod * fac[i] % mod * f[S - i][T] % mod );
    	write( ans ), putchar( '
    ' );
    	return 0;
    }
    
  • 相关阅读:
    Git -- 分支管理简介
    Git -- 从远程库克隆
    Git -- 添加远程仓库
    C# sha256 加密算法
    如何将IOS版本的更新下载文件指向到自己的服务器
    如何让windows服务器IIS支持.apk/.ipa文件下载
    vistual studio 去除 git 源代码 绑定
    Redis 环境搭建与使用(C#)
    c#图片添加水印
    C#使用WSDL服务总结
  • 原文地址:https://www.cnblogs.com/crashed/p/13509234.html
Copyright © 2011-2022 走看看