您的位置:首页 > 其它

hdu 5449 Robot Dog(期望+lca)

2015-09-29 16:09 411 查看

hdu 5449 Robot Dog(期望+lca)

题目链接:hdu 5449 Robot Dog

解题思路

n有50000,询问次数有100,每次询问的路径点数最多有100,对于不同询问去做动态规划,开一个dp[u][i]表示在第u个节点匹配了i个的期望,显然最坏情况下dp数组的每个状态都要遍历到,复杂度为o(nqp),不能接受。

换个想法,如果我们能知道从节点i走到节点j的期望值dis[[i][j],那么对于每次询问只要按照treasure的位置顺序依次加上相应的期望值即可。不过n很大,预处理每棵树的复杂度为o(n2),也不能接受。但是dis[i][j]的值其实就从节点i到节点j路径上边的期望权值(注意dis[i][j]≠dis[j][i])所以,如果我们能分别算出每条边双向移动的代价,剩下的利用树的前缀性质就可以在log(n)的复杂度内计算出路径上的期望值。

既以0节点为根,处理出up数组(每个节点移动到节点0的代价),dn数组(0移动到每个节点的代价),然后对于路径u−>v,权值

w=up[u]−up[lca(u,v)]+dn[v]−dn[lca(u,v)]

现在问题只剩如何求一条边的移动代价,假设我们求从u节点移动到u的父亲节点,即up[u](son[u]表示u节点的孩子节点个数,直接相连的,Su表示u节点孩子节点的集合,直接相连)

up[u]=1+∑i∈Su(up[i]+up[u])son[u]+1

两边同乘son[u]+1,得:

(son[u]+1)∗up[u]=son[u]+1+∑i∈Su(up[i]+up[u])

(son[u]+1)∗up[u]=son[u]+1+∑i∈Su(up[i])+son[u]∗up[u]

up[u]=son[u]+1+∑i∈Su(up[i])

如果u为叶子节点,那么son[u] = 0,up[u]=1

如果u不为叶子节点,那么将up[i]一直展开,最后肯定剩下的是叶子节点。在展开的过程中,一定会遍历到以u节点为根的子树中的所有节点,并且只遍历一次。(sz[u]表示u子树的节点个数,Tu表示u子树节点的集合)

up[u]=∑i∈Tu(son[i]+1)

up[u]=∑i∈Tu(son[i])+sz[u]

在u子树中,每个节点都是唯一节点的孩子节点(直接连接),除了根以外,所以∑i∈Tu(son[i])=sz[u]−1,带回得:

up[u]=2∗sz[u]−1

同理可求dn[u]

代码

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long ll;
const int maxn = 50005;

int N, E, first[maxn], jump[maxn<<1], linker[maxn<<1];
ll sz[maxn], up[maxn], dn[maxn], f[20][maxn], dep[maxn];

void addEdge(int u, int v) {
jump[E] = first[u];
linker[E] = v;
first[u] = E++;
}

void dfs (int u, int far, int d) {
sz[u] = 1;
dep[u] = d;
f[0][u] = far;

for (int i = first[u]; i + 1; i = jump[i]) {
int v = linker[i];
if (v == far) continue;
dfs(v, u, d + 1);
sz[u] += sz[v];
}
}

void dfs (int u) {
if (f[0][u] != -1) {
up[u] = up[f[0][u]] + 2 * sz[u] - 1;
dn[u] = dn[f[0][u]] + 2 * (N-sz[u]) - 1;
} else
up[u] = dn[u] = 0;

for (int i = first[u]; i + 1; i = jump[i]) {
int v = linker[i];
if (v == f[0][u]) continue;
dfs(v);
}
}

int lca(int u, int v) {
if (dep[u] < dep[v]) swap(u, v);
int d = dep[u] - dep[v];
for (int i = 0; i < 20; i++) if ((1<<i)&d)
u = f[i][u];

if (u == v) return u;
for (int i = 19; i >= 0; i--) {
if (f[i][u] != f[i][v]) {
u = f[i][u];
v = f[i][v];
}
}
return f[0][u];
}

void init () {
E = 0;
memset(first, -1, sizeof(first));

int u, v;
scanf("%d", &N);
for (int i = 1; i < N; i++) {
scanf("%d%d", &u, &v);
addEdge(u, v);
addEdge(v, u);
}
dfs(0, -1, 0);
dfs(0);

for (int k = 1; k < 20; k++) {
for (int i = 0; i < N; i++)
f[k][i] = f[k-1][f[k-1][i]];
}
}

ll get (int u, int v) {
int l = lca(u, v);
return up[u] - up[l] + dn[v] - dn[l];
}

void solve () {
int q, p, u, v;
scanf("%d", &q);
while (q--) {
ll ans = 0;
scanf("%d%d", &p, &u);
while (p--) {
scanf("%d", &v);
ans += get(u, v);
u = v;
}
printf("%lld.0000\n", ans);
}
}

int main () {
int cas;
scanf("%d", &cas);
while (cas--) {
init();
solve();
if (cas) printf("\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  hdu