Codeforces Round #778 D. Potion Brewing Class(数组模拟 lcm)

链接:D. Potion Brewing Class
题意:
给一颗树,知道相邻节点权值的比值,求树的所有节点的权值和的最小值(每个节点权值都是整数)。
思路:

  1. 如果确定1号节点的权值为 1 , 那么所有节点的值都可以求出来,但要求是整数,所以我们只要求出把每个权值化为最简分数后所有分母的lcm,最后把每个节点的值都乘这个lcm即可。
  2. 这个思路不难想,但实现起来有点麻烦(比赛写了个爆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;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值