树 \operatorname{树} 树
题目链接: SSL比赛 1457 \operatorname{SSL比赛\ 1457} SSL比赛 1457
题目
梦游中的你来到了一棵 N N N 个节点的树上. 你一共做了 Q Q Q 个梦, 每个梦需要你从点 u u u 走到点 v v v 之后才能苏醒, 由于你正在梦游, 所以每到一个节点后,你会在它连出去的边中等概率地 选择一条走过去, 为了确保第二天能够准时到校, 你要求出每个梦期望经过多少条边才能苏 醒. 为了避免精度误差, 你要输出答案模 1 0 9 + 7 10^9 + 7 109+7 的结果.
输入
第一行两个整数分别代表 N N N 和 Q Q Q 。 接下来 N − 1 N-1 N−1 行, 每行两个整数 u , v u, v u,v 代表树中的一条边. 接下来 Q Q Q 行, 每行两个整数代表询问的 u , v . u,v. u,v.
输出
一共 Q Q Q 行, 每行一个整数代表答案
样例输入
4 2
1 2
2 3
3 4
1 4
3 4
样例输出
9
5
数据范围
对于
20
%
20\%
20% 的数据,
N
<
=
10.
N <= 10.
N<=10.
对于
40
%
40\%
40% 的数据,
N
<
=
1000.
N <= 1000.
N<=1000.
另有
20
%
20\%
20% 的数据, 保证给定的树是一条链.
对于
100
%
100\%
100% 的数据,
N
<
=
100000
,
Q
<
=
100000.
N <= 100000, Q <= 100000.
N<=100000,Q<=100000.
思路
这道题满分 80 80 80 ,两个数据有问题(好像)。
这道题是一道 LCA 加推论,极其可怕。
推论极其可怕,可以看这位大佬的博客。
代码
#include<cstdio>
#include<cmath>
#include<algorithm>
#define mo 1000000007
using namespace std;
struct node {
int to, nxt;
}e[200001];
int n, q, x, y, ru[100001], le[100001], KK, dep[100001], fa[21][100001], f[100001], g[100001], meet, ans, deg[100001];
bool in[100001];
void add(int x, int y) {
e[++KK] = (node){y, le[x]}; le[x] = KK;
e[++KK] = (node){x, le[y]}; le[y] = KK;
}
void dfsf(int father, int now) {//求出f数组
for (int i = le[now]; i; i = e[i].nxt)//记录这个点有多少个儿子
deg[now]++;
f[now] = deg[now];
for (int i = le[now]; i; i = e[i].nxt)
if (e[i].to != father) {
dfsf(now, e[i].to);
f[now] = (f[now] + f[e[i].to]) % mo;
}
}
void dfsg(int father, int now) {//求出g数组
int sum = deg[now];
for (int i = le[now]; i; i = e[i].nxt)
if (e[i].to != father) sum = (sum + f[e[i].to]) % mo;
else sum = (sum + g[now]) % mo;
for (int i = le[now]; i; i = e[i].nxt)
if (e[i].to != father) {
g[e[i].to] = ((sum - f[e[i].to]) % mo + mo) % mo;
dfsg(now, e[i].to);
}
}
void dfsqian(int father, int now) {//把两个数组前缀和(从根到点的和)
dep[now] = dep[father] + 1;
fa[0][now] = father;
for (int i = le[now]; i; i = e[i].nxt)
if (e[i].to != father) {
f[e[i].to] = (f[now] + f[e[i].to]) % mo;
g[e[i].to] = (g[now] + g[e[i].to]) % mo;
dfsqian(now, e[i].to);
}
}
int LCA(int x, int y) {//LCA
if (dep[x] < dep[y]) swap(x, y);
for (int i = (log2(n)) + 1; i >= 0; i--)
if (dep[fa[i][x]] >= dep[y])
x = fa[i][x];
if (x == y) return x;
for (int i = (log2(n)) + 1; i >= 0; i--)
if (fa[i][x] != fa[i][y]) {
x = fa[i][x];
y = fa[i][y];
}
return fa[0][x];
}
int main() {
scanf("%d %d", &n, &q);//读入
for (int i = 1; i <= n - 1; i++) {
scanf("%d %d", &x, &y);
add(x, y);//连边
}
dfsf(0, 1);
dfsg(0, 1);
dfsqian(0, 1);
for (int i = 1; i <= 20; i++)//预处理出父亲
for (int j = 1; j <= n; j++)
fa[i][j] = fa[i - 1][fa[i - 1][j]];
for (int i = 1; i <= q; i++) {
scanf("%d %d", &x, &y);//读入
meet = LCA(x, y);//LCA
printf("%d\n", ((f[x] - f[meet]) % mo + (g[y] - g[meet]) % mo) % mo);//求出答案
}
return 0;
}