zoukankan      html  css  js  c++  java
  • [NOI2012]迷失游乐园(期望,动态规划)

    [NOI2012]迷失游乐园(期望,动态规划)

    题目大意

    给定一颗基环树或一颗树,从随机一个点出发,每次等概率走向之前没走过的点,知道不能走为止,求路径期望长度

    数据范围

    对于 (100\%) 的数据,(1leq W_ileq 100)

    测试点编号 (n) (m) 备注
    (1) (n=10) (m = n-1) 保证图是链状
    (2) (n=100) (m = n-1) 只有节点 (1) 的度数大于 (2)
    (3) (n=1000) (m = n-1) /
    (4) (n=10^5) (m = n-1) /
    (5) (n=10^5) (m = n-1) /
    (6) (n=10) (m = n) /
    (7) (n=100) (m = n) 环中节点个数 (leq 5)
    (8) (n=1000) (m = n) 环中节点个数 (leq 10)
    (9) (n=10^5) (m = n) 环中节点个数 (leq 15)
    (10) (n=10^5) (m = n) 环中节点个数 (leq 20)

    解题思路

    对于前 50% 部分--树

    (e[x]) 为从 x 开始走的路径期望长度,(d[x]) 为从 x 开始一直向子树走的路径期望长度

    (d[x]) 可以简单树形 dp 得到 (d[x] = sum_{y in subtreee(x)}frac {d[y]+w[x][y]}{son[x]})

    e[x] 考虑换根 dp,维护从 x 向上走的期望长度 res,(res=frac{e[x]*(sons[x] + 1)-(d[y]+w[x][y])}{sons[x]} + w[x][y]),x 为根时要特殊讨论一下,(e[x] = frac{res + d[x]*sons[x]}{sons[x] + 1}),x 为根时 (e[x] = d[x])

    带码

    void dfs1(int x, int fa) {
    	for (int i = h[x]; i; i = ne[i]) {
    		int y = to[i]; if (y == fa) continue;
    		dfs1(y, x); d[x] += d[y] + w[i], sons[x]++;
    	}
    	if (sons[x]) d[x] /= sons[x];
    }
    
    void dfs2(int x, int fa, double res) {
    	if (x != 1) e[x] = (d[x] * sons[x] + res) / (sons[x] + 1);
    	else e[x] = d[x];
    	for (int i = h[x]; i; i = ne[i]) {
    		int y = to[i]; if (y == fa) continue;
    		if (x != 1) dfs2(y, x, (e[x] * (sons[x] + 1) - (d[y] + w[i])) / sons[x] + w[i]); 
    		else {
    			if (sons[x] > 1) dfs2(y, x, (e[x] * sons[x] - (d[y] + w[i])) / (sons[x] - 1) + w[i]);
    			else dfs2(y, x, w[i]);
    		}
    	}
    }
    

    对于剩下 50% 的数据--基环树

    基环树首先应该做的就是把环单独考虑,我们可以想象成将一些有根树的根串起来成一个环,先对每棵树求一遍 d[x] 数组,再求出从每个根开始走环的期望路径长度,最后再换根 dp 从根转移到其他节点即可,细节:根的 sons要+=2

    带码

    const int N = 200500;
    int h[N], ne[N<<1], to[N<<1], w[N<<1], tot = 1;
    inline void add(int x, int y, int z) {
    	ne[++tot] = h[x], to[tot] = y, w[h[x] = tot] = z;
    }
    
    int vis[N], rt, Rt, sons[N], m, n;
    double d[N], e[N];
    void dfs1(int x, int fa) {
    	for (int i = h[x]; i; i = ne[i]) {
    		int y = to[i]; if (vis[y] || y == fa) continue;
    		dfs1(y, x); d[x] += d[y] + w[i], sons[x]++;
    	}
    	if (sons[x]) d[x] /= sons[x];
    }
    
    void dfs2(int x, int fa, double res) {
    	if (x != Rt) e[x] = (d[x] * sons[x] + res) / (sons[x] + 1);
    	for (int i = h[x]; i; i = ne[i]) {
    		int y = to[i]; if (y == fa || vis[y]) continue;
    		if (x != Rt) dfs2(y, x, (e[x] * (sons[x] + 1) - (d[y] + w[i])) / sons[x] + w[i]); 
    		else {
    			if (sons[x] > 1) dfs2(y, x, (e[x] * sons[x] - (d[y] + w[i])) / (sons[x] - 1) + w[i]);
    			else dfs2(y, x, w[i]);
    		}
    	}
    }
    
    int To[N], Fr[N], wei[N];
    void get_cir(int x, int fa) {
    	vis[x] = 1;
    	for (int i = h[x]; i; i = ne[i]) {
    		int y = to[i]; if (i == fa) continue;
    		if (rt) return; To[x] = y, Fr[y] = x, wei[y] = w[i];
    		if (vis[y]) rt = y; get_cir(y, i ^ 1);
    	}
    }
    
    void work(int x) {
    	double p = 1;
    	for (int nw = To[x]; nw != x; nw = To[nw]) {
    		e[x] += p * wei[nw], p /= sons[nw] + 1;
    		e[x] += p * sons[nw] * d[nw];
    		if (To[nw] == x) e[x] += p * d[nw];
    	}
    	p = 1;
    	for (int nw = Fr[x]; nw != x; nw = Fr[nw]) {
    		e[x] += p * wei[To[nw]], p /= sons[nw] + 1;
    		e[x] += p * sons[nw] * d[nw];
    		if (Fr[nw] == x) e[x] += p * d[nw];
    	}
    	
    	e[x] = (e[x] + d[x] * sons[x]) / (sons[x] + 2);
    	sons[x] += 2, dfs2(Rt = x, 0, 0), sons[x] -= 2;
    }
    
    int main() {
    	read(n), read(m);
    	for (int i = 1, x, y, z; i <= m ; i++) 
    		read(x), read(y), read(z), add(x, y, z), add(y, x, z);
    	if (m == n - 1) {
    		Rt = 1, dfs1(1, 0), e[1] = d[1], dfs2(1, 0, 0);
    		double ans = 0;
    		for (int i = 1;i <= n; i++) ans += e[i];
    		printf ("%.6lf
    ", ans / n);
    		return 0;
    	}
    	get_cir(1, 0); memset(vis, 0, sizeof(vis));
    	for (int x = rt; !vis[x]; x = To[x]) vis[x] = 1;
    	dfs1(rt, 0);
    	for (int x = To[rt]; x != rt; x = To[x]) dfs1(x, 0);
    	memset(vis, 0, sizeof(vis));
    	for (int x = rt; !vis[x]; x = To[x]) vis[x] = 1;
    	work(rt);
    	for (int x = To[rt]; x != rt; x = To[x]) work(x);
    	double ans = 0;
    	for (int i = 1;i <= n; i++) ans += e[i];
    	printf ("%.6lf
    ", ans / n);
    	return 0;
    }
    
  • 相关阅读:
    linux命令: mount
    梳理一下uboot是如何从nandflash挂载文件系统的
    MDK的优化应用
    面向对象设计思想:面向对象设计的基本原则
    问题
    nodejs安装不了和npm安装不了的解决方法
    []: secureCRT连接ubuntu问题- The remote system refused the connection
    字符设备驱动[深入]:linux cdev详解
    使用MDK将STM32的标准库编译成lib使用
    liteos任务(二)
  • 原文地址:https://www.cnblogs.com/Hs-black/p/12807868.html
Copyright © 2011-2022 走看看