题目大意:有一棵n个点的根为1的树,对于每个点求这个点与其他每个点的lca的编号连续相乘后与多少个后缀0
思路:因为只有2和5相乘才会有后缀0,所以只有质因子中有2或5的点作为Lca才有贡献,而且会贡献给当前点的所有子节点,对他的一个相邻的子节点的贡献为当前贡献+自己含有的2/5的个数*(当前点子节点数-下一个点子节点数),所以我们首先dfs遍历每个点,求出所有点的子节点的数量(包括自己),然后再dfs,同时维护当前的2和5的贡献,每个点的答案等于2的贡献加当前点有几个2因子乘以当前点字节点数和对5进行同样计算的最小值,
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
bool vis[N];
vector<int>g[N];
int sonnum[N];
int get2(int x)
{//计算x有多少质因子2
int ret = 0;
while (x%2==0)
{
x >>= 1;
ret++;
}
return ret;
}
int get5(int x)
{//计算x有多少质因子5
int ret = 0;
while (x %5==0)
{
x /= 5;
ret++;
}
return ret;
}
void dfs(int u)
{//计算每个点的子节点数
vis[u] = 1;
for (int i = 0; i < g[u].size(); i++)
{
int v = g[u][i];
if (!vis[v])
{
dfs(v);
sonnum[v]++;//帮下层点记录它自己
sonnum[u] += sonnum[v];//接收下层的子节点数
}
}
}
long long ans[N];
bool vis2[N];
void dfs2(int u, int cn2, int cn5)
{//当前点,2的贡献,5的贡献
vis2[u] = 1;
ans[u] = min(cn2 + get2(u) * sonnum[u], cn5 + get5(u) * sonnum[u]);
//当前点的答案就是之前的贡献加上自己的贡献,取2和5的最小值
for (int i = 0; i < g[u].size(); i++)
{
int v = g[u][i];
if (!vis2[v])
{
dfs2(v, cn2+get2(u) * (sonnum[u] - sonnum[v]), cn5 + get5(u) * (sonnum[u] - sonnum[v]));//传给下一层的贡献就是当前贡献+当前点因子数*(当前点字节点数-下一个点子节点数)
}
}
}
int main()
{
int n, q;
cin >> n >> q;
for (int i = 1; i <= n - 1; i++)
{
int u, v;
scanf("%d%d", &u, &v);
g[u].push_back(v);
g[v].push_back(u);//邻接表存图
}
dfs(1);
sonnum[1]++;//1要加上自身
dfs2(1, 0, 0);
for (int i = 1; i <= q; i++)
{
int x;
scanf("%d", &x);
printf("%lld\n", ans[x]);
}
return 0;
}
然后将每个点的贡献向下传递