zoukankan      html  css  js  c++  java
  • [ARC113F]Social Distance

    题目

    点这里看题目。

    分析

    首先需要知道,在此题中连续随机变量的期望可以如下计算:

    [E(X)=int_{0}^{+infty} P(t< X)mathop{}!mathrm d t ]

    关于这个东西的理解:

    首先考虑一般的离散随机变量 (X) ,它有 (n) 个取值 (0,delta,2delta,3delta,dots,(n-1)delta) ,其中 (delta) 满足 (delta=frac{M}{n}) ,且 (M) 为常数。那么 它的期望就是:

    [egin{aligned} E(x)=&sum_{k=0}^{n-1}kdelta imes P(X=kdelta)\ =&sum_{k=0}^{n-1}P(X=kdelta)sum_{j=0}^{k-1}delta\ =&sum_{j=0}^{n-1}deltasum_{k=j+1}^{n-1}P(X=kdelta)\ =&sum_{j=0}^{n-1}delta imes P(jdelta<X) end{aligned} ]

    考虑 (n ightarrow +infty) 的时候,可以发现:

    [E(X)=int_{0}^{M}mathop{}!mathrm dt imes P(t< X) ]

    由于 (X) 的取值被限制在 ([0,M)) 里面,所以积分上界也可以被修改。

    在此题中,我们直接设 (X) 为任意两个人之间距离的最小值,那么考虑 (t) 确定的时候该如何计算 (P(t<X)) 。很显然,如果设第 (k) 个人的位置为 (p_k) (其中 (k)(0sim N-1) ),那么必有:

    [forall 0le k<N-1,p_{k+1}-p_{k}>t ]

    适当地做一下变形,让变量独立:设 (p'_{k}=p_k-kt) ,那么就有:

    [forall 0le k<N-1,p'_{k+1}>p'_k ]

    此时每个 (p') 也有其对应的取值区间,那么问题就变成了:

    给定 (n) 个区间,有 (n) 个连续随机变量,第 (k) 个的取值范围为 ([l_k,r_k]) 。求这 (n) 个随机变量组成的序列为单调递增的概率。

    这是经典的问题。我们可以根据所有的 ([l_k,r_k]) 的端点,将所有可用值划分为若干段区间,其中第 (j) 段为 ([L_j,R_j)) ,于是可以设计 DP :

    (f_{i,j}) 表示前 (i) 个随机变量组成的序列递增,且第 (i) 个随机变量落在第 (j) 个区间里的概率。

    不难写出转移:

    [f_{i,j}=sum_{k=1}^{i}left(left(sum_{l=1}^{j-1}f_{k-1,l} ight) imes left(prod_{p=k}^i[[L_j,R_j)sube [l_p,r_p]]frac{R_j-L_j}{r_p-l_p} ight) ight) ]

    使用前缀和优化,转移可以做到 $O(n^3w) $,其中 (w) 是对元素做单次运算的复杂度。

    但是 (t) 实际上是个变量,我们不可能对每一个 (t) 都计算一遍。不过可以注意到,转移的方式和 ([l_k,r_k]) 这些端点的相对位置有关,而转移的结果才和 (t) 有关。根据转移,我们可以发现在转移方式确定的时候,转移结果是 (t) 的多项式,那么这样就很好处理了,对于同样的转移方式,我们可以得出结果多项式;同时不难发现同样转移方式对应的 (t) 必然是一段区间,因此可以直接算出该段的积分结果。

    其一:不太清楚这里可不可以代入 (n+1) 个点,从而直接维护多项式的点值,这样加法、乘法都是 (O(n)) 的;

    其实这里乘的就是一次函数,本身乘法就是 (O(deg))

    其二:可以发现这里的 (P(t<X)) 其实就是一个分段函数,分段函数自然就要分段积分。

    这里转移方式对应的就是区间端点相对位置,可以发现区间端点相对位置只会变化 (O(n^2)),因此 (t) 的区间也只有 (O(n^2)) 段。每一段暴力计算,时间复杂度即是 (O(n^6))

    小结:

    1. 关于连续随机变量,用得虽然不多,但是一定要了解,不然这道题都下不去手
    2. 其中关于区间的转化其实挺巧的,主要还是一个分离变量的思想
    3. 这里将 DP 的含义变为了对多项式进行 DP ,虽然还是在应对 (t) 是连续的限制,不过这也提醒了我们:
      1. DP 的值理论上可以是任何东西,只要能满足一定的运算即可
      2. DP 的转移方式和转移结果并不是绑在一块的东西——通过向 DP 值中引入变量我们可以让它们两个脱钩。

    代码

    #include <cstdio>
    #include <cstring>
    #include <utility>
    #include <algorithm>
    using namespace std;
    
    #define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
    #define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
    
    typedef long long LL;
    typedef pair<double, int> Point;
    
    const double eps = 1e-6;
    const int mod = 998244353, inv2 = mod + 1 >> 1;
    const int MAXN = 35;
    
    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' );
    }
    
    template<typename _T>
    _T ABS( const _T a )
    {
    	return a < 0 ? -a : a;
    }
    
    inline LL Gcd( LL, LL );
    inline int Mul( int, int );
    inline int Sub( int, int );
    inline int Add( int, int );
    inline int Inv( const int );
    
    struct Fraction
    {
    	LL a, b;
    	Fraction() { a = 0, b = 1; }
    	Fraction( int V ) { a = V, b = 1; }
    	int operator () () const { return Mul( a, Inv( b ) ); }
    	Fraction( int A, int B ) { LL d = Gcd( A, B ); a = A / d, b = B / d; }
    	bool operator < ( const Fraction &g ) const { return a * g.b < b * g.a; }
    	bool operator != ( const Fraction &g ) const { return a ^ g.a || b ^ g.b; }
    	bool operator == ( const Fraction &g ) const { return a == g.a && b == g.b; }
    };
    
    int smallInv[MAXN];
    
    struct Poly
    {
    	int coe[MAXN], n;
    	Poly() { n = 0, memset( coe, 0, sizeof coe ); }
    	Poly( int B ) { memset( coe, 0, sizeof coe ), coe[n = 0] = B; }
    	Poly( int K, int B ) { memset( coe, 0, sizeof coe ), coe[n = 1] = K, coe[0] = B; }
    	Poly& operator /= ( const int &g ) { return *this = *this / g; }
    	Poly& operator *= ( const int &g ) { return *this = *this * g; }
    	Poly& operator *= ( const Poly &g ) { return *this = *this * g; }
    	Poly& operator += ( const Poly &g ) { return *this = *this + g; }
    	Poly& operator -= ( const Poly &g ) { return *this = *this - g; }
    	
    	int operator () ( const int x ) const
    	{
    		int ret = 0;
    		for( int i = 0, pw = 1 ; i <= n ; i ++, pw = Mul( pw, x ) )
    			ret = Add( ret, Mul( coe[i], pw ) );
    		return ret;
    	}
    	
    	Poly operator + ( const Poly &g ) const
    	{
    		Poly ret; 
    		for( int &k = ret.n ; k <= n || k <= g.n ; k ++ ) ret.coe[k] = Add( coe[k], g.coe[k] );
    		for( int &k = ret.n ; k && ! ret.coe[k] ; k -- );
    		return ret;
    	}
    	
    	Poly operator - ( const Poly &g ) const 
    	{
    		Poly ret; 
    		for( int &k = ret.n ; k <= n || k <= g.n ; k ++ ) ret.coe[k] = Sub( coe[k], g.coe[k] );
    		for( int &k = ret.n ; k && ! ret.coe[k] ; k -- );
    		return ret;
    	}
    	
    	Poly operator * ( const int d ) const
    	{
    		Poly ret;
    		for( int &k = ret.n ; k <= n ; k ++ ) ret.coe[k] = Mul( coe[k], d );
    		for( int &k = ret.n ; k && ! ret.coe[k] ; k -- );
    		return ret;
    	}
    	
    	Poly operator / ( const int d ) const
    	{
    		Poly ret; int v = Inv( d );
    		for( int &k = ret.n ; k <= n ; k ++ ) ret.coe[k] = Mul( coe[k], v );
    		for( int &k = ret.n ; k && ! ret.coe[k] ; k -- );
    		return ret;
    	}
    
    	Poly Integral() const
    	{
    		Poly ret; ret.n = n + 1;
    		rep( i, 0, n ) ret.coe[i + 1] = Mul( coe[i], smallInv[i + 1] );
    		return ret;
    	}
    
    	Poly operator * ( const Poly &g ) const 
    	{
    		Poly ret; ret.n = n + g.n;
    		rep( i, 0, n ) rep( j, 0, g.n )
    			ret.coe[i + j] = Add( ret.coe[i + j], Mul( coe[i], g.coe[j] ) );
    		for( int &k = ret.n ; k && ! ret.coe[k] ; k -- );
    		return ret;
    	}
    };
    
    Poly dp[MAXN][MAXN << 1];
    
    Point pnt[MAXN << 1];
    
    Fraction seq[MAXN * MAXN * 4];
    int fac[MAXN], ifac[MAXN];
    int X[MAXN], fir[MAXN], lst[MAXN];
    int N, tot = 0;
    
    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 LL Gcd( LL a, LL b ) { return b ? Gcd( b, a % b ) : a; }
    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;
    }
    
    int Integral( const Poly &f, const int L, const int R ) 
    { return Sub( f.Integral()( R ), f.Integral()( L ) ); }
    
    Poly Length( const int id ) 
    {
    	Poly l, r;
    	int idL = pnt[id].second, idR = pnt[id + 1].second;
    	l = Poly( - ABS( idL ), idL < 0 ? X[- idL] : X[idL - 1] );
    	r = Poly( - ABS( idR ), idR < 0 ? X[- idR] : X[idR - 1] );
    	return r - l; 
    }
    
    int Calc( const Fraction lef, const Fraction rig )
    {
    	double dif = 1.0 * lef.a / lef.b + eps;
    	int len = 0;
    	rep( i, 0, N - 1 ) 
    		pnt[++ len] = Point( X[i] - i * dif, i + 1 ), 
    		pnt[++ len] = Point( X[i + 1] - i * dif, - i - 1 );
    	sort( pnt + 1, pnt + 1 + len );
    	rep( i, 1, len )
    	{
    		if( pnt[i].second > 0 ) fir[pnt[i].second - 1] = i;
    		else lst[- pnt[i].second - 1] = i - 1;
    	}
    	rep( i, 0, N - 1 ) rep( j, 0, len - 1 ) dp[i][j] = Poly();
    	rep( i, fir[0], lst[0] ) dp[0][i] = Length( i ) / ( X[1] - X[0] );
    	rep( i, 1, len - 1 ) dp[0][i] += dp[0][i - 1];
    	Poly tmp, poss;
    	rep( i, 1, N - 1 ) 
    	{
    		rep( j, fir[i], lst[i] )
    		{
    			poss = 1;
    			for( int k = i ; ~ k && fir[k] <= j && j <= lst[k] ; k -- )
    			{
    				if( ! k ) tmp = 1;
    				else tmp = dp[k - 1][j - 1];
    				poss *= Length( j ) / ( X[k + 1] - X[k] );
    				dp[i][j] += poss * tmp * ifac[i - k + 1];
    			}
    		}
    		rep( j, 1, len - 1 ) dp[i][j] += dp[i][j - 1];
    	}
    	int L = lef(), R = rig();
    	return Integral( dp[N - 1][len - 1], L, R );
    }
    
    void Init( const int n )
    {
    	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 );
    	smallInv[1] = 1; rep( i, 2, n ) smallInv[i] = Mul( smallInv[mod % i], mod - mod / i );
    }
    
    int main()
    {
    	read( N ), Init( N + 1 );
    	rep( i, 0, N ) read( X[i] );
    	rep( i, 0, N - 1 )
    		rep( j, i + 1, N - 1 )
    		{
    			seq[++ tot] = Fraction( X[j] - X[i], j - i );
    			seq[++ tot] = Fraction( X[j + 1] - X[i + 1], j - i );
    			seq[++ tot] = Fraction( X[j + 1] - X[i], j - i );
    			seq[++ tot] = Fraction( X[j] - X[i + 1], j - i );
    		}
    	sort( seq + 1, seq + 1 + tot );
    	tot = unique( seq + 1, seq + 1 + tot ) - seq - 1;
    	
    	seq[++ tot] = 1e7; int ans = 0;
    	rep( i, 1, tot ) 
    		ans = Add( ans, Calc( seq[i], seq[i + 1] ) );
    	write( ans ), putchar( '
    ' );
    	return 0;
    }
    
  • 相关阅读:
    规划
    学习规划
    续约
    每日一记
    每日记录
    《代码大全》第八章 防御式编程
    《代码大全》第七章
    平安夜
    每日一记
    培养良好的生活习惯
  • 原文地址:https://www.cnblogs.com/crashed/p/14664307.html
Copyright © 2011-2022 走看看