Potion Brewing Class ( 思维,分解质因子)

本文详细解析了一道关于树形结构的数学优化问题,要求构造整数序列,使得序列中每对节点间的比例关系满足题目给定条件,并且整个序列和尽可能小。解决方案通过深度优先搜索和质因数分解来确定每个节点的权值,最终通过求解最简形式的分母最小公倍数来找到最小和。代码实现中涉及到了动态规划和数论中的快速幂运算。
摘要由CSDN通过智能技术生成

Potion Brewing Class

[Link](Problem - D - Codeforces)

题意

让你构造 n n n个满足给出 n − 1 n-1 n1个关系的整数,并使和最小,关系如 i , j , x , y {i,j,x,y} i,j,x,y表示 a i a j = x y \frac{a_i}{a_j} =\frac{x}{y} ajai=yx,题目保证一定有解。

思路

​ 即给你一棵树,给每个点赋值,使得满足边关系,且和最小。我们假设 1 1 1为根权值为 1 1 1,从一开始往下 d f s dfs dfs并按照边比例转移,这样会得到一些分数,例如 [ 1 , 1 3 , 8 9 , 2 3 , 4 5 , 2 4 ] [{1, \frac{1}{3},\frac{8}{9},\frac{2}{3},\frac{4}{5},\frac{2}{4}}] [1,31,98,32,54,42],也就是要给所有的数都乘一个数使之变成整数,就是要乘以这些分数的最简形式的分母的最小公倍数,直接这么做不好做,数太大了。

​ 我们只需要知道 1 1 1最后的权值应该是多少就可以了,其它的搜一下就可以。我们可以从 1 1 1开始往下搜,用 c n t [ x ] cnt[x] cnt[x]来维护当前 x x x出现了多少次,更新 m x c n t [ x ] : x 需 要 多 少 个 mxcnt[x]:x需要多少个 mxcnt[x]x,具体来说例如 [ i , j , x , y ] [i,j,x,y] [i,j,x,y],从 [ i → j ] [i\to j] [ij]需要 v [ j ] = v [ i ] × y x v[j]=v[i]\times \frac{y}{x} v[j]=v[i]×xy,因此对于 y y y的每个质因子我们 c n t [ y ] − 质 因 子 的 次 数 cnt[y]-质因子的次数 cnt[y], x 的 每 个 质 因 子 我 们 c n t [ x ] + 质 因 子 的 次 数 x的每个质因子我们cnt[x]+质因子的次数 xcnt[x]+ x x x是分母因此用 x x x来更新每个质因子至少需要多少个,最后暴力枚举一下更新 1 1 1的权值,然后搜一下其他点,记录答案即可。

Code

#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 2e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 998244353;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
//23123213
map<int, int> mp[N];
int qmi(int a, int b) {
    a %= mod;
    int res = 1;
    while (b) {
        if (b & 1) res = (LL)res * a % mod;
        a = (LL)a * a % mod;
        b >>= 1;
    }
    return res;
}
int inv(int x) {
    return qmi(x, mod - 2);
}

void solve() {
    cin >> n;
    vector<array<int, 3>> g[n + 10];
    vector<int> mxcnt(n + 10);
    vector<int> cnt(n + 10);

    for (int k = 1; k < n; k ++) {
        int i, j, x, y; cin >> i >> j >> x >> y;
        g[i].push_back({j, x, y});
        g[j].push_back({i, y, x});
    }
    function<void(int, int)> dfs = [&](int u, int fa) {
        for (auto [v, x, y] : g[u]) {
            if (v == fa) continue ;
            for (auto [i, j] : mp[y]) cnt[i] -= j;
            for (auto [i, j] : mp[x]) {
                cnt[i] += j;
                mxcnt[i] = max(mxcnt[i], cnt[i]);
            }
            dfs(v, u);
            for (auto [i, j] : mp[y]) cnt[i] += j;
            for (auto [i, j] : mp[x]) cnt[i] -= j;
        }
    };  
    dfs(1, -1);
    int t = 1;
    for (int i = 2; i <= n; i ++)
        for (int j = 1; j <= mxcnt[i]; j ++) t = (LL)t * i % mod;

    LL res = 0;
    function<void(int, int, int)> sum = [&](int u, int fa, int t) {
        res = (res + t) % mod;
        for (auto [v, x, y] : g[u]) {
            if (v == fa) continue ;
            int tt = (LL) t * y % mod * inv(x) % mod;
            sum(v, u, tt);
        }
    };

    sum(1, -1, t);

    cout << res << '\n';
}
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    for (int i = 2; i <= N - 1; i ++) {
        int x = i;
        for (int j = 2; j <= sqrt(x); j ++) {
            while (x % j == 0) mp[i][j] ++, x /= j;
        }
        if (x > 1) mp[i][x] ++;
    }
    int T;
    cin >> T;
    while (T -- ) {
        solve();
    }
    return 0;
}
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值