Live2D

Solution -「Gym 102979E」Expected Distance

Description

  Link.

  用给定的 {an1},{cn} 生成一棵含有 n 个点的树,其中 u 连向 [1,u) 中的某个 v,概率为 ava1+a2++au1,边权为 cu+cv。并给出 q 组询问 (ui,vi),每次回答 uivi 的树上距离的期望。答案对 (109+7) 取模。

  n,q3×105

Solution

Defining LATEX macros...

  问题卡壳,必有结论。

  令 1 为根,把 dist(u,v) 转化成 dist(1,u)+dist(1,v)2dist(lca(u,v))。记 f(u)=E(dist(1,u)),显然有

f(u)=cu+1su1v<uav(fv+cv).

其中 si=j=1iai,可见 f 可以轻易地 O(n) 求出。我们接下来研究 dist(lca(u,v))。不妨设 u<v,可以发现一个结论:

v>u, E(dist(lca(u,v)))=g(u).

其中 g(u) 是仅与 u 有关的量。

证明   考虑求 lca(u,v) 的方式,在 v 沿着祖先跳跃时,我们只关心第一次使得 vu 的位置。此时仅有两种情况
  • v=u,概率为 ausu
  • v<u,概率为 su1su

  可见与 v 无关。

  在证明的基础上,亦能得到 g(u) 的转移:

g(u)=1su(aucu+v<uavgv).

也能 O(n) 求出,所以本题就解决啦。

Code

/*~Rainybunny~*/

#include <bits/stdc++.h>

#define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
#define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i )

const int MAXN = 3e5, MOD = 1e9 + 7;
int n, q, a[MAXN + 5], s[MAXN + 5], invs[MAXN + 5];
int c[MAXN + 5], f[MAXN + 5], g[MAXN + 5];

inline int mul( const int a, const int b ) { return 1ll * a * b % MOD; }
inline int sub( int a, const int b ) { return ( a -= b ) < 0 ? a + MOD : a; }
inline int add( int a, const int b ) { return ( a += b ) < MOD ? a : a - MOD; }
inline int mpow( int a, int b ) {
    int ret = 1;
    for ( ; b; a = mul( a, a ), b >>= 1 ) ret = mul( ret, b & 1 ? a : 1 );
    return ret;
}

int main() {
    std::ios::sync_with_stdio( false ), std::cin.tie( 0 );

    std::cin >> n >> q;
    rep ( i, 1, n - 1 ) {
        std::cin >> a[i], s[i] = a[i] + s[i - 1];
        invs[i] = mpow( s[i], MOD - 2 );
    }
    rep ( i, 1, n ) std::cin >> c[i];

    for ( int i = 2, pre = mul( a[1], c[1] ); i <= n; ++i ) {
        f[i] = add( c[i], mul( invs[i - 1], pre ) );
        pre = add( pre, mul( a[i], add( f[i], c[i] ) ) );
    }

    for ( int i = 2, pre = 0; i < n; ++i ) {
        g[i] = mul( invs[i], add( mul( a[i], f[i] ), pre ) );
        pre = add( pre, mul( a[i], g[i] ) );
    }

    for ( int u, v; q--; ) {
        std::cin >> u >> v;
        if ( u > v ) u ^= v ^= u ^= v;
        if ( u == v ) std::cout << "0\n";
        else std::cout << sub( add( f[u], f[v] ), mul( 2, g[u] ) ) << '\n';
    }
    return 0;
}

posted @   Rainybunny  阅读(195)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示