• 「Gym102979E」Expected Distance


    题目

    点这里看题目。

    分析

    对答案变形:

    [sum_{ein P}c_{e_u}+c_{e_v}=2sum_{win P,w ot=u,w ot= v}c_w+c_u+c_v ]

    因此我们需要关心的是最终路径上非 (u) 且非 (v) 的点的点权和。

    假设 (u<v),我们先画一条路径来分析一下:

    graph.png

    考虑路径出现的概率,对于任何一条边 ((p,q),p<q),设 (s_n=sum_{i=1}^na_i),则该边出现的概率为 (frac{a_p}{s_{q-1}})。每条边的概率之积则为该路径出现的概率。而我们可以重新排列,使得各个点贡献独立。比如上面这条路径,(u,v) 分别贡献 (s^{-1}_{u-1},s_{v-1}^{-1})(lca) 会贡献 (a^2_{lca}),而路径上的点 (win {a,b,c,d,e}) 则会贡献 (frac{a_w}{s_{w-1}})

    所以,当我们确定了 (u,v,lca) 之后,剩余的点可出现可不出现,系数易于计算。因此自然的思路是尝试枚举 (Psetminus{u,v,lca}),反向构造出路径来。

    在此之前,我们需要明确当路径以 (lca) 为根的时候,路径在标号上满足堆性质。因此假如我们知道了 (u)(lca) 上的点和 (v)(lca) 上的点,则两条链上点的顺序是确定的。那么对于 (win Psetminus {u,v,lca}),如果有 (w<u),则在反向构建的时候,(w) 既可以被放入 (u) 链,也可以被放入 (v) 链,贡献 2 的系数;而如果 (u<w<v),则 (w) 不能被放入 (u) 链,只能被放入 (v) 链,贡献 1 的系数。

    此外,注意到概率是乘法的关系,而最终的点权需要求和,自然的想法便是将点权放到指数上,使用概率生成函数。

    因此,对于路径 ((u,v)),我们可以构建 (F_k(x))(lca=k) 时该路径的点权的概率生成函数,根据上面的分析不难得出:

    [egin{aligned} F_u(x)&=frac{a_u}{s_{v-1}}prod_{u<j<v}left(1+frac{a_j}{s_{j-1}}x^{c_j} ight)\ F_k(x)&=frac{a_k^2x^{c_k}}{s_{u-1}s_{v-1}}prod_{k<j<u}left(1+frac{2a_j}{s_{j-1}}x^{c_j} ight)prod_{u<j<v}left(1+frac{a_j}{s_{j-1}}x^{c_j} ight),1le k<u end{aligned} ]

    根据结论,计算出 (sum_{1le kle u}F_k'(1)) 就是答案,使用若干个前缀数组记录一下即可。

    计算量确实比较大,但是带入值的时候,由于 (c_jge 1),细节不多。

    小结:

    1. 注意对问题下手的角度,这里是对链本身考虑,而另一种更简单的方法是将贡献拆分成 (dep_{u}+dep_{v}-2dep_{lca}) 的类似的形式,于是就转化到了祖先与子孙上。虽然这个做法比较复杂,但是我觉得挺漂亮的

    2. 注意细节,算错好多次导致浪费了不少时间,比如最开始的时候就没有意识到 2 的方案系数。这种题可以通过手玩关注到不少细节。

      另一方面,这其实也说明计数的思路不太完整,忽略了路径的点在分配时候带来的方案系数。这个其实随便画一画 (n=4) 都可以发现好嘛

    代码

    #include <cstdio>
    #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 = 1e9 + 7;
    const int MAXN = 3e5 + 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 P[MAXN], Pder[MAXN];
    int H[MAXN], Hder[MAXN];
    int T[MAXN], TDer[MAXN];
    int R[MAXN], RDer[MAXN];
    int pre[MAXN], preDer1[MAXN], preDer2[MAXN];
    
    int a[MAXN], s[MAXN], c[MAXN], invS[MAXN];
    int N, Q;
    
    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 Sqr( const int x ) { return Mul( x, 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 main()
    {
    	read( N ), read( Q );
    	rep( i, 1, N - 1 ) read( a[i] );
    	rep( i, 1, N ) read( c[i] );
    	rep( i, 1, N ) invS[i] = Inv( s[i] = Add( s[i - 1], a[i] ) );
    	R[0] = H[0] = 1;
    	rep( i, 1, N )
    	{
    		T[i] = Add( 1, Mul( a[i], invS[i - 1] ) );
    		TDer[i] = Mul( Mul( a[i], c[i] ), invS[i - 1] );
    		R[i] = Mul( R[i - 1], T[i] );
    		RDer[i] = Add( Mul( RDer[i - 1], T[i] ), Mul( R[i - 1], TDer[i] ) );
    		
    		P[i] = Add( 1, Mul( Mul( 2, a[i] ), invS[i - 1] ) );
    		Pder[i] = Mul( Mul( Mul( 2, a[i] ), c[i] ), invS[i - 1] );
    		H[i] = Mul( H[i - 1], P[i] );
    		Hder[i] = Add( Mul( Hder[i - 1], P[i] ), Mul( H[i - 1], Pder[i] ) );
    		
    		int inv = Inv( H[i] );
    		pre[i] = Add( pre[i - 1], Mul( inv, Sqr( a[i] ) ) );
    		preDer1[i] = Add( preDer1[i - 1], Mul( Sqr( inv ), Mul( Hder[i], Sqr( a[i] ) ) ) );
    		preDer2[i] = Add( preDer2[i - 1], Mul( inv, Mul( Sqr( a[i] ), c[i] ) ) );
    	}
    	while( Q -- )
    	{
    		int u, v;
    		read( u ), read( v );
    		if( u > v ) std :: swap( u, v );
    		if( u == v ) { puts( "0" ); continue; }
    		int ans = 0, res = 0, G;
    		ans = Mul( Mul( a[u], invS[v - 1] ), Mul( Sqr( Inv( R[u] ) ), 
    				   Sub( Mul( RDer[v - 1], R[u] ), Mul( R[v - 1], RDer[u] ) ) ) );
    		G = Mul( Sqr( Inv( R[u] ) ), Sub( Add( Mul( Mul( Hder[u - 1], R[v - 1] ), R[u] ),
    											   Mul( Mul( H[u - 1], RDer[v - 1] ), R[u] ) ), 
    											   Mul( Mul( H[u - 1], R[v - 1] ), RDer[u] ) ) );
    		res = Add( res, Mul( G, pre[u - 1] ) );
    		G = Mul( Mul( H[u - 1], R[v - 1] ), Inv( R[u] ) );
    		res = Add( res, Mul( G, Sub( preDer2[u - 1], preDer1[u - 1] ) ) );
    		ans = Add( ans, Mul( res, Mul( invS[u - 1], invS[v - 1] ) ) );
    		
    		write( Add( Mul( ans, 2 ), Add( c[u], c[v] ) ) ), putchar( '
    ' );
    	}
    	return 0;
    }
    
  • 相关阅读:
    【python进阶】哈希算法(Hash)
    【数据库】MongoDB操作命令
    【数据库】MongoDB安装&配置
    【python基础】元组方法汇总
    【python基础】集合方法汇总
    滴水穿石-04Eclipse中常用的快捷键
    滴水穿石-03一道面试题引发的思考
    滴水穿石-02制作说明文档
    滴水穿石-01JAVA和C#的区别
    步步为营101-同一个PCode下重复的OrderNumber重新排序
  • 原文地址:https://www.cnblogs.com/crashed/p/15205007.html
Copyright © 2020-2023  润新知