zoukankan      html  css  js  c++  java
  • HDU5449 Robot Dog

    题目蓝链

    Description

    给你一棵树,经过一条边的时间为(1)。有(q)次询问,每次询问给定一条有若干个关键点的路径,你需要依次经过这些关键点。你会从路径的起点开始,每次随机选择一条与当前点直接相连的边走过去。问你走完这条路径的期望时间为多少

    Solution

    首先,本题有这样一个性质(在树上)

    对于任意一条链((x, y)),我们在链上任意取一个点(k),均满足(dis(x, k) + dis(k, y) = dis(x, y)),其中(dis(x, y))表示(x)点到(y)的期望时间

    我们可以任选一个点作为根,然后预处理出两个数组(up[i], down[i]),分别表示(i)号点到它父亲需要的期望时间以及(i)号点父亲到它所需要的期望时间

    然后我们就可以维护(up)(down)的树上前缀和就可以求出任意一条路径的期望时间了

    下面是一种比较简单的求出(up)(down)的方法,(size[i])表示以(i)为根的子树大小

    [up[i] = 2 cdot size[i] - 1 \ down[i] = 2 cdot (n - size[i]) - 1 ]

    还有一种比较容易理解,但理解比较麻烦的办法 传送门

    Code

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define fst first
    #define snd second
    #define mp make_pair
    #define squ(x) ((LL)(x) * (x))
    #define debug(...) fprintf(stderr, __VA_ARGS__)
    
    typedef long long LL;
    typedef pair<int, int> pii;
    
    template<typename T> inline bool chkmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; }
    template<typename T> inline bool chkmin(T &a, const T &b) { return a > b ? a = b, 1 : 0; }
    
    inline int read() {
    	int sum = 0, fg = 1; char c = getchar();
    	for (; !isdigit(c); c = getchar()) if (c == '-') fg = -1;
    	for (; isdigit(c); c = getchar()) sum = (sum << 3) + (sum << 1) + (c ^ 0x30);
    	return fg * sum;
    }
    
    const int maxn = 5e4 + 10;
    
    vector<int> g[maxn];
    
    int n, m, d[maxn], sz[maxn], fa[maxn][16];
    
    inline void dfs(int now, int f) {
    	sz[now] = 1, d[now] = d[f] + 1, fa[now][0] = f;
    	for (int i = 1; i <= 15; i++) fa[now][i] = fa[fa[now][i - 1]][i - 1];
    	for (int i = 0; i < g[now].size(); i++) {
    		int son = g[now][i];
    		if (son == f) continue;
    		dfs(son, now);
    		sz[now] += sz[son];
    	}
    }
    
    LL up[maxn], dn[maxn];
    inline void DFS(int now, int f) {
    	up[now] = (sz[now] << 1) - 1, dn[now] = ((n - sz[now]) << 1) - 1;
    	up[now] += up[f], dn[now] += dn[f];
    	for (int i = 0; i < g[now].size(); i++) {
    		int son = g[now][i];
    		if (son == f) continue;
    		DFS(son, now);
    	}
    }
    
    inline int lca(int x, int y) {
    	if (d[x] < d[y]) swap(x, y);
    	for (int i = 15; ~i; i--)
    		if (d[fa[x][i]] >= d[y]) x = fa[x][i];
    	if (x == y) return x;
    	for (int i = 15; ~i; i--)
    		if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
    	return fa[x][0];
    }
    
    inline LL dis(int x, int y) {
    	int LCA = lca(x, y);
    	return up[x] - up[LCA] + dn[y] - dn[LCA];
    }
    
    int main() {
    	freopen("C.in", "r", stdin);
    	freopen("C.out", "w", stdout);
    
    	int T = read();
    	while (T--) {
    		n = read();
    		for (int i = 1; i <= n; i++) g[i].clear();
    		for (int i = 1; i < n; i++) {
    			int x = read() + 1, y = read() + 1;
    			g[x].push_back(y), g[y].push_back(x);
    		}
    		dfs(1, 0), DFS(1, 0);
    		m = read();
    		for (int i = 1; i <= m; i++) {
    			LL ans = 0;
    			int cnt = read(), lst = read() + 1;
    			for (int j = 1; j <= cnt; j++) {
    				int now = read() + 1;
    				ans += dis(lst, now);
    				lst = now;
    			}
    			printf("%lld.0000
    ", ans);
    		}
    		if (T) printf("
    ");
    	}
    
    	return 0;
    }
    
  • 相关阅读:
    吊打XXX
    [CQOI2011]动态逆序对
    陌上花开
    【BOI2007】摩基亚Mokia
    [SCOI2008]奖励关
    最小生成树
    打表
    【中学高级本】倒酒
    整数合并
    韩信点兵
  • 原文地址:https://www.cnblogs.com/xunzhen/p/10327942.html
Copyright © 2011-2022 走看看