链接:D. Potion Brewing Class
题意:
给一颗树,知道相邻节点权值的比值,求树的所有节点的权值和的最小值(每个节点权值都是整数)。
思路:
- 如果确定1号节点的权值为 1 , 那么所有节点的值都可以求出来,但要求是整数,所以我们只要求出把每个权值化为最简分数后所有分母的lcm,最后把每个节点的值都乘这个lcm即可。
- 这个思路不难想,但实现起来有点麻烦(比赛写了个爆ll的自暴自弃,现在写完发现好像也不难写),因为分母的范围是会超过 long long的,所以我们用数组记录没遍历完一个节点后的lcm值(数组记录每一个质因子出现的次数)。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 7;
typedef long long ll;
const int mod = 998244353;
int n , T , z[maxn] , m[maxn];
int num[maxn];
ll pr = 0 , nx = 1 , ny = 1;
struct node{
ll v , x , y;
};
vector<node> e[maxn];
ll poww(ll a ,ll b){
ll ans = 1;
while(b){
if(b & 1){
ans = ans * a % mod;
}
a = a * a % mod;
b >>= 1;
}
return ans;
}
void up(int pos){ //保证为最简分数
int x = min(z[pos] , m[pos]);
z[pos] -= x;
m[pos] -= x;
num[pos] = max(num[pos] , m[pos]);
}
void addx(int val){
for(int i = 2; i * i <= val; i ++){
while(val % i == 0){
val /= i;
z[i] ++;up(i);
}
}
if(val != 1){
z[val] ++;up(val);
}
}
void addy(int val){
for(int i = 2; i * i <= val; i ++){
while(val % i == 0){
val /= i;
m[i] ++;up(i);
}
}
if(val != 1){
m[val] ++;up(val);
}
}
void dfs(int u , int pre){
pr = pr + nx * poww(ny , mod - 2) % mod; //先记录不乘lcm时的答案。
pr %= mod;
for(int i = 0 ; i < e[u].size(); i ++){
ll to = e[u][i].v;
ll x = e[u][i].x;
ll y = e[u][i].y;
if(to == pre) continue;
nx = nx * y % mod;
ny = ny * x % mod;
addx(y);
addy(x);
dfs(to , u);
addx(x); //回溯的时候只需要反过来乘一下即可。
addy(y);
nx = nx * x % mod;
ny = ny * y % mod;
}
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i = 1; i <= n; i ++){
e[i].clear();
z[i] = m[i] = num[i] = 0;
}
pr = 0;
nx = ny = 1;
for(int i = 1,u,v,x,y; i < n; i ++){
scanf("%d%d%d%d",&u,&v,&x,&y);
e[u].push_back({v , x , y});
e[v].push_back({u , y , x});
}
dfs(1 , -1);
for(int i = 1; i <= n; i ++){
pr = pr * poww(i , num[i]) % mod;
}
printf ("%lld\n",pr);
}
return 0;
}