Potion Brewing Class(CF 1654 D)

传送门

题目大意

\(t\)组询问,每组询问给你一棵有\(n\)个节点,\(n-1\)条边的树,树上每两个相连点之间的权值成比例,每组询问问所有权值都是整数且最小时的权值和,答案对\(998244353\)取模。\((1\leq t\leq10^4,2\leq n\leq2\cdot10^5,\sum n\leq2\cdot10^5)\)

思路

首先很容易想到\(dfs\)一下然后从叶子节点传答案上来,但是发现不是能写,于是就换一个思路。这次我们设根节点的权值是\(1\),然后往下\(dfs\),于是就可以得到\(n\)个最简分数,然后约个分就结束了。但是再仔细想一想,真的嘛?很容易又能想到分母还是会很大然后爆\(long\ long\),然后就会寄。所以我们又能想到一次一次更新,每到达一个新的点,我们可以分别对分子分母进行质因子分解,然后记一个\(a[i]\)\(b[i]\)分别表示质因子\(i\)的个数和质因子\(i\)出现过的最多次数,对于分母的质因子,我们在原基础上加上他们的个数,对于分子就减去,然后最后就可以先把每个点对于模数取模后的值加起来,然后再乘上分母的\(LCM\),然后这道题就能轻松\(AC\)啦!

代码

#include<bits/stdc++.h>
using namespace std;
long long mod=998244353;
const int maxn=200005;
struct EDGE
{
    int next,to,x,y;
}edge[maxn<<1];
int head[maxn];
int cnt;
void add(int u,int v,int x,int y)
{
    edge[cnt].x=x;
    edge[cnt].y=y;
    edge[cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
long long a[200005],b[200005];
long long poww(long long a,long long n)
{
	long long ans=1;
	while(n)
	{
		if(n&1)ans=ans*a%mod;
		a=a*a%mod;
		n>>=1;
	}
	return ans;
}
int main()
{
	memset(head,-1,sizeof(head));
	int _;
	scanf("%d",&_);
	while(_--)
	{
		int n;
		scanf("%d",&n);
		for(int i=1;i<n;i++)
		{
			int u,v,x,y;
			scanf("%d%d%d%d",&u,&v,&x,&y);
			add(u,v,x,y);
			add(v,u,y,x);
		}
		int maxx=0;
		long long tot=0;
		function<void(int,int)>update=[&](int x,int w)
		{
			for(int i=2;i*i<=x;i++)
			{
				while(x%i==0)
				{
					a[i]+=w;
					x/=i;
				}
				if(a[i]>b[i])
				{
					b[i]=a[i];
					maxx=max(maxx,i);
				}
			}
			if(x!=1)
			{
				a[x]+=w;
				if(a[x]>b[x])
				{
					b[x]=a[x];
					maxx=max(maxx,x);
				}
			}
		};
		function<void(int,int,long long)>dfs=[&](int s,int fa,long long val)
		{
			tot=(tot+val)%mod;
			for(int i=head[s];~i;i=edge[i].next)
			{
				int it=edge[i].to,x=edge[i].x,y=edge[i].y;
				if(it==fa)continue;
				update(y,-1);
				update(x,1);
				dfs(it,s,val*y%mod*poww(x,mod-2)%mod);
				update(x,-1);
				update(y,1);
			}
		};
		dfs(1,0,1);
		for(int i=2;i<=maxx;i++)
		{
			if(b[i])
			{
				tot=tot*poww(i,b[i])%mod;
				b[i]=0;
			}
		}
		printf("%lld\n",tot);
		if(_)
		{
			for(int i=1;i<=cnt;i++)
				edge[i]={0,0,0,0};
			for(int i=1;i<=n;i++)
				head[i]=-1;
			cnt=0;
		}
	}
	return 0;
}
posted @ 2022-03-21 21:56  Jerry_Black  阅读(151)  评论(0)    收藏  举报