zoukankan      html  css  js  c++  java
  • [洛谷] 仓鼠找sugar II(期望DP)

    Problem

    题目地址

    题意简述:树上任意两点 (a,b),从 (a) 随机游走到 (b) 的期望步数。(n le 10^5)

    应该是一个经典题,为什么只有洛谷上能找到。

    Solution

    终点,注意到每个点的转移方向只有向父亲或向儿子两种,可以将期望的状态定义设计为转移向一边的期望(f[u]) 表示 (u->fa) 的期望步数,即从 (u) 向上走一步的期望步数。则有:

    [f[u] = frac{1}{d_u} + frac{sum (f[v]+f[u]+1)}{d_u} ]

    (其中 (d_u) 表示 (u) 的度数)

    移项,则有:

    [f[u] = d_u + sum f[v] ]

    特别的,假设根为 (x),有 (f[x]=0)

    (g(x))表示根为(x)(即终点为 (x))时的答案,(dep'[u]) 表示 (u->x) 的期望步数。根据定义:(g(x)=sum dep'[u]),其中:(dep'[u]=dep'[fa]+f[u])。进一步我们可以发现,(g(x)=sum f[u]*sz_u) 其中 (sz_u) 表示 (u) 的子树大小。

    由于(x) 可以是 (1 sim n) 的任意一个点,所以我们需要算出 (g(x),1 le x le n)。于是我们可以写一个换根dp

    假设 (u) 是当前的根,(v)(u) 的儿子且是下一个根。可以发现根从 (u) 换到 (v)(f[ ]) 数组中仅有 (f[u])(f[v]) 发生了改变,同样的,只有 (sz_u)(sz_v) 发生了改变。具体的

    [f[u] = sum-f[v], f[v]=0 ]

    (其中 (sum = sum d_u)

    所以,(u) 对答案的贡献从 (0) 变成了 ((sum-f[v])(n-sz_v)),而 (v) 对答案的贡献从 (f[v]*sz_v) 变成了 (0)具体的

    [g(v)=g(u)+(sum-f[v])(n-sz_v)-f[v]*sz_v ]

    用两遍 (Dfs) 即可实现,时间复杂度 (O(n))

    Code

    Talk is cheap.Show me the code.

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    inline int read() {
        int x = 0, f = 1; char ch = getchar();
        while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
        return x * f;
    }
    const int N = 100007, mod = 998244353;
    int n,cnt,sum;
    int f[N],g[N],sz[N],deg[N];
    int head[N];
    struct Edge {
    	int next,to;
    }edge[N<<1];
    inline void add(int u,int v) {
    	edge[++cnt] = (Edge)<%head[u],v%>;
    	head[u] = cnt;
    }
    inline void Add(int &x,int y) {
    	x = (x+y>mod ? x+y-mod : x+y);
    }
    inline void Dec(int &x,int y) {
    	x = (x-y<0 ? x-y+mod : x-y);
    }
    void Dfs1(int u,int fa) {
    	sz[u] = 1; f[u] = deg[u];
    	for(int i=head[u];i;i=edge[i].next) {
    		int v = edge[i].to;
    		if(v != fa) {
    			Dfs1(v,u); sz[u] += sz[v]; f[u] += f[v];
    		}
    	}
    }
    void Dfs2(int u,int fa) {
    	for(int i=head[u];i;i=edge[i].next) {
    		int v = edge[i].to;
    		if(v != fa) {
    			Add(g[v], g[u]); Dec(g[v], f[v]*sz[v]%mod); Add(g[v], (sum-f[v])*(n-sz[v])%mod);
    			Dfs2(v,u);
    		}
    	}
    }
    int Pow(int x,int y) {
    	int res = 1, base = x;
    	while(y) {
    		if(y&1) res = res*base%mod; base = base*base%mod; y >>= 1;
    	}
    	return res;
    }
    signed main()
    {
    	n = read();
    	for(int i=1;i<n;++i) {
    		int u = read(), v = read();
    		add(u,v), add(v,u); ++deg[u], ++deg[v];
    	}
    	Dfs1(1,0);
    	f[1] = 0;
    	for(int i=1;i<=n;++i) Add(g[1], f[i]*sz[i]%mod);
    	for(int i=1;i<=n;++i) sum += deg[i];
    	Dfs2(1,0);
    	int ans = 0;
    	for(int i=1;i<=n;++i) Add(ans, g[i]);
    	ans = ans*Pow(n*n%mod, mod-2)%mod;
    	printf("%lld
    ",ans);
    	return 0;
    }
    /*
    3
    1 2
    1 3
    
    110916041
    */
    

    Summary

    经典题,没啥好说的。

    • 将期望的状态定义设计为转移向一边的期望”,具体一点,可以描述为 “(i -> i+1) 的期望步数

    • 换根dp别想半天都想不通。。。

  • 相关阅读:
    GridView动态删除Item
    GridView动态添加View
    Unity debug mode下的watermark去除
    Espresso unit test的输入法问题
    [troubleShooting]如何安装Andorid M preview到NEXUS系列机器
    [trouble-shooting]android 无法启动X86模式虚拟机的问题解决。
    Android studio+ndk 下面的gradle配置记录
    html call android apk的方法汇总
    Android studio 运行和编译的优化
    Android Installation error: INSTALL_FAILED_UID_CHANGED的解决办法
  • 原文地址:https://www.cnblogs.com/BaseAI/p/14001534.html
Copyright © 2011-2022 走看看