zoukankan      html  css  js  c++  java
  • [HDU6848]Expectation

    题目

    校内赛的改编题目,题意基本与[HDU6848]Expectation相同。

    分析

    首先,不难发现,本题就是求所有不同的操作序列的距离和,最后乘上 (frac{1}{n!2^n}) 的概率就是答案。

    于是考虑如何求这个距离和。这里有两种方法:

    方法 1

    本题的贡献显然是可以拆分的。我们只需要计算 (x_{i+1}-x_i) 会被算几次就好了。

    显然每一段的次数仅与数量 (n) 和段数 (i) 相关,因此可以定义状态:

    (f(n,i)) :有 (n)(n+1) 洞时,(x_{i+1}-x_i) 的计算次数。

    先画出示意图如下,我们考虑求第 (3) 段的次数。

       o     o     o
       
    U     U     U     U
    |--|--|--|--|--|--|
     1  2  3  4  5  6
    

    其中 o 表示球,U 表示洞。下方标注了每一段。

    那么我们随便让一个球进洞:

       o     o     o
            /
    U     U     U     U
    |--|--|--|--|--|--|
     1  2  3  4  5  6
    --------Then-------
       o           o
               
    U           U     U
    |--|--------|--|--|
     1  2        3  4
    

    可以发现,现在球和洞各减少了一个,并且原先的第 (3) 段现在被包在了第 (2) 段里面。因此这样的话会转移到 (f(n-1,i-1)) ,仅有一种情况。

    再考虑剩下两种情况:

       o     o     o
         
    U     U     U     U
    |--|--|--|--|--|--|
     1  2  3  4  5  6
    --------Then-------
             o     o
       
    U           U     U
    |--------|--|--|--|
     1        2  3  4
    

    现在它转移到了 (f(n-1,i-2)) 里面,对应了 (i-1) 种转移的情况。

       o     o     o
                  /
    U     U     U     U
    |--|--|--|--|--|--|
     1  2  3  4  5  6
    --------Then-------
       o     o      
                  
    U     U           U
    |--|--|--|--------|
     1  2  3  4
    

    现在它转移到了 (f(n-1,i)) 里面,对应了 (2n-i) 种转移的情况。

    接着考虑当前这一步带来的次数。当我们将 (i+1) 移到 (i) 里面(或者 (i) 移到 (i+1) 里面)的时候,我们就会带来 1 次的贡献,总共就会带来 ((i-1)! imes 2^{i-1}) 的贡献。

    因此可以得到转移:

    [f(n,i)=(i-1)f(n-1,i-2)+f(n-1,i-1)+(2n-i)f(n-1,i)+(i-1)! imes 2^{i-1} ]

    于是这个就可以 (O(n^2)) 预处理了。最后每次 (O(n)) 扫一遍就可以计算答案了。

    方法 2

    该方法仅针对改编后的题目,仅可以通过一次询问的情况

    方案 2.1:请参考 zxy 巨佬的博客

    方案 2.2:

    这是本人口胡的做法,并且还没有优化出来

    可以发现,最后只有一个洞会留下来。我们可以枚举这个洞,这样左右两边就是独立的,且洞数恰好等于球数。

    因此可以定义 (f(i,j)) 表示区间 ([i,j]) 被消完的操作序列的距离和,且 (i)(j) 的奇偶性不同

    (i)(j) 奇的转移为例。我们可以枚举 (i) 进的洞 (k) ,那么 ((i+1,k-1)) 必须在 (i) 被操作之前被消完。而 ((k+1,j)) 的操作则任意。可以发现我们得到了两个子问题。

    注意到合法序列的数量仅与区间长度有关。因此设 (g(i)) 表示长度为 (i) 的区间的合法操作序列的个数。令 (L)([i,j]) 中球的个数, (l)([i,k]) 中球的个数。可以得到转移:

    [f(i,j)=sum_{k} inom{L}{l} imes (g(L-l-1)f(i+1,k-1)+g(l)g(L-l-1)(x_k-x_i)+g(l)f(k+1,j)) ]

    由于是统计序列,所以需要乘上组合数计算序列的形态。

    这样转移是 (O(n^3)) 的,如果有会优化的请一定要告诉我。

    小结:

    方法 1 的关键在于拆分贡献独立计算提取影响状态的信息

    方法 2 ,作为一个未通过的做法......

    代码

    #include <cstdio>
    
    typedef long long LL;
    
    const int mod = 998244353;
    const int MAXN = 3005;
    
    template<typename _T>
    void read( _T &x )
    {
    	x = 0; char s = getchar(); int f = 1;
    	while( s < '0' || '9' < s ) { 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 f[MAXN][MAXN << 1];
    
    int X[MAXN];
    int N, inv;
    
    int Qkpow( int, int );
    int Inv( const int a ) { return Qkpow( a, mod - 2 ); }
    int Sub( int x, int v ) { return x < v ? x + mod - v : x - v; }
    int Mul( LL x, int v ) { x *= v; if( x >= mod ) x %= mod; return x; }
    int Add( int x, int v ) { return x + v >= mod ? x + v - mod : x + v; }
    int Fix( const int a ) { return ( a % mod + mod ) % mod; }
    
    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 )
    {
    	int coe = 1;
    	f[1][1] = f[1][2] = 1;
    	for( int i = 2 ; i <= n ; i ++ )
    	{
    		coe = Mul( 2 * ( i - 1 ), coe );
    		for( int j = 1 ; j <= 2 * i ; j ++ )
    		{
    			f[i][j] = Add( f[i - 1][j - 1], Mul( f[i - 1][j], 2 * i - j ) );
    			if( j >= 2 ) f[i][j] = Add( f[i][j], Mul( f[i - 1][j - 2], j - 1 ) );
    			f[i][j] = Add( f[i][j], coe );
    		}
    	}
    	inv = Inv( Mul( coe, 2 * n ) );
    }
    
    int main()
    {
    	read( N ), Init( N );
    	for( int i = 1 ; i <= 2 * N + 1 ; i ++ ) read( X[i] );
    
    	int ans = 0;
    	for( int i = 1 ; i <= N ; i ++ )
    		ans = Add( ans, Add( Mul( Fix( X[i << 1] - X[( i << 1 ) - 1] ), f[N][( i << 1 ) - 1] ),
    							 Mul( Fix( X[( i << 1 ) + 1] - X[i << 1] ), f[N][i << 1] ) ) );
    	write( Mul( ans, inv ) ), putchar( '
    ' );
    	return 0;
    }
    
  • 相关阅读:
    京东POP2020新店新规
    hdu 1203
    hdu 1513
    有关Canvas的一点小事—canvas数据和像素点
    有关Canvas的一点小事—图像绘制
    有关Canvas的一点小事--鼠标绘图
    Javascript和jquery事件--键盘事件KeyboardEvent
    [转]DOM0,DOM2,DOM3事件处理方式区别
    Javascript和jquery事件--鼠标事件的小结
    Javascript和jquery事件--鼠标移动事件mousemove
  • 原文地址:https://www.cnblogs.com/crashed/p/13777006.html
Copyright © 2011-2022 走看看