Codeforces 1654D. Potion Brewing Class

传送门
\(\texttt{Difficulty:2100}\)

题目大意

有一个长为 \(n(2\le n\le 2\cdot10^5)\) 的正整数序列 \(a\) ,有 \(n-1\) 条比例关系,每条关系 \(i,j,x,y(1≤i,j≤n, i≠j, 1≤x,y≤n)\) ,表示 \(\frac{a_i}{a_j}=\frac{x}{y}\) ,这 \(n-1\) 条比例关系可以唯一确定序列中各个数之间的倍数关系,求 \(\sum a_i\) 的最小值 \(mod\space 998244353\)

思路

因为这 \(n-1\) 条比例关系可以唯一确定序列中各个数之间的倍数关系,所以给定的关系肯定是一棵树。于是我们在确定了任意一个节点的数值后,其余节点也就随之确定,于是我们可以令 \(a_1\) 为根,其值为 \(1\), 于是其他节点都可以得到一个与 \(a_1\) 的比值,因为需要值为正整数,有要求最后总和最小,所以我们将所有乘上所有这些比值中分母的 \(lcm\) 即可,因为 \(lcm\) 可能非常大,于是我们通过记录每个分母各个质因子的个数, \(lcm\) 的各个质因子的个数就是它们在分母中个数的最大值,我们可以先预处理出 \(1\sim n\) 间所有数字的质因数分解形式,用 \(map\) 来存储,之后先进行一遍 \(dfs\) ,每次计算完与 \(a_1\) 的比例后进行约分,我们只记录分母的质因数分解,如果某个质因子的个数 \(<0\) 就说明分子上有相应个数的质因数而分母上没有,我们将 \(lcm\) 的每个质因子个数初始化为 \(0\) 就可以正常处理,之后用分母的每个质因子更新 \(lcm\) 的答案。得到 \(lcm\) 之后我们进行第二遍 \(dfs\) 此时直接让对应的比例不用约分直接乘以 \(lcm\) 直接计算即可,同时对所有节点的值累加,最后即为答案,复杂度 \(O(n\sqrt n)\)

代码

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define pb push_back
//#define int LL
//#define lc p*2
//#define rc p*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
//#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 200010;

LL T, N, ans, mul;
struct edge {
	int to;
	LL x, y;
};
vector<edge>G[maxn];
map<LL, int>mp[maxn], cnt;
int mx[maxn];

LL qpow(LL a, LL x)
{
	LL res = 1;
	while (x)
	{
		if (x & 1)
			res = res * a % mod;
		a = a * a % mod;
		x >>= 1;
	}

	return res;
}

LL inv(LL x)
{
	return qpow(x, mod - 2);
}

void calc(LL x)
{
	LL k = x;
	for (LL i = 2; i * i <= x; i++)
	{
		while (x % i == 0)
		{
			x /= i;
			mp[k][i]++;
		}
	}
	if (x != 1)
		mp[k][x]++;
}

void add_edge(int from, int to, int x, int y)
{
	G[from].push_back(edge{ to,x,y });
	G[to].push_back(edge{ from, y,x });
}

void dfs1(int v, int p)
{
	for (auto& [to, x, y] : G[v])
	{
		if (to == p)
			continue;
		for (auto& [a, b] : mp[y])
			cnt[a] -= b;
		for (auto& [a, b] : mp[x])
		{
			cnt[a] += b;
			mx[a] = max(mx[a], cnt[a]);
		}
		dfs1(to, v);
		for (auto& [a, b] : mp[y])
			cnt[a] += b;
		for (auto& [a, b] : mp[x])
			cnt[a] -= b;
	}
}

void dfs2(int v, int p, LL val)
{
	ans = (ans + val * mul % mod) % mod;
	for (auto& [to, x, y] : G[v])
	{
		if (to == p)
			continue;
		LL tmp = val * y % mod * inv(x) % mod;
		dfs2(to, v, tmp);
	}
}

void solve()
{
	dfs1(1, 0);
	for (int i = 1; i <= N; i++)
		mul = mul * qpow(i, mx[i]) % mod;
	dfs2(1, 0, 1);
	cout << ans << endl;
}

int main()
{
	IOS;
	for (int i = 2; i <= 200000; i++)
		calc(i);
	cin >> T;
	while (T--)
	{
		cin >> N;
		for (int i = 1; i <= N; i++)
			G[i].clear(), mx[i] = 0;
		cnt.clear();
		ans = 0, mul = 1;
		LL i, j, x, y;
		for (int a = 1; a < N; a++)
		{
			cin >> i >> j >> x >> y;
			add_edge(i, j, x, y);
		}
		solve();
	}

	return 0;
}
posted @ 2022-05-17 23:33  Prgl  阅读(41)  评论(0)    收藏  举报