NOIP模拟42:卷

  一道很水的树规题,考场AC。
  考虑到乘积很大,会爆\(long long\),而取模后的数不可以用来比较大小,所以考虑给数挂个\(log\),将乘化为加。
  然后另开一个数组记录乘积取模的结果即可。
  将数挂\(log\)是在有乘积出现时的常用做法,因为乘积很可能会爆掉,而又要比较大小的话\(log\)是很合适的,并且对于大于1的数他是增函数,可以直接用来比较大小。
  挂完log正常树规就好,很常规的方程,就不写了。

Code
#include<bits/stdc++.h>
using namespace std;
namespace STD
{
	#define rr register
	typedef long long ll;
	const double eps=1e-5;
	const int inf=INT_MAX;
	const int mod=1e9+7;
	const int N=2e5+4;
	int n;
	int w[N],to[N<<1],dire[N<<1],head[N];
	int reco[N][2];
	double c[N][2];
	inline void add(int f,int t)
	{
		static int num=0;
		to[++num]=t;
		dire[num]=head[f];
		head[f]=num;
	}
	int read()
	{
		rr int x_read=0,y_read=1;
		rr char c_read=getchar();
		while(c_read<'0'||c_read>'9')
		{
			if(c_read=='-') y_read=-1;
			c_read=getchar();
		}
		while(c_read<='9'&&c_read>='0')
		{
			x_read=(x_read<<3)+(x_read<<1)+(c_read^48);
			c_read=getchar();
		}
		return x_read*y_read;
	}
	void DP(int x,int f)
	{
		reco[x][1]=1;
		reco[x][0]=1;
		for(int i=head[x];i;i=dire[i])
		{
			if(to[i]==f) continue;
			DP(to[i],x);
			c[x][1]+=c[to[i]][0];
			reco[x][1]=((ll)reco[x][1]*(ll)reco[to[i]][0])%mod;
			if(c[to[i]][1]-c[to[i]][0]>=eps)
			{
				c[x][0]+=c[to[i]][1];
				reco[x][0]=((ll)reco[x][0]*(ll)reco[to[i]][1])%mod;
			}
			else
			{
				c[x][0]+=c[to[i]][0];
				reco[x][0]=((ll)reco[x][0]*(ll)reco[to[i]][0])%mod;
			}
		}
		c[x][1]+=log2(w[x]);
		reco[x][1]=((ll)reco[x][1]*(ll)w[x])%mod;
	}
};
using namespace STD;
int main()
{
	n=read();
	for(int i=1;i<=n;i++) w[i]=read();
	for(int i=1;i<n;i++)
	{
		int u=read(),v=read();
		add(u,v),add(v,u);
	}
	DP(1,0);
	if(c[1][0]-c[1][1]>=eps) printf("%d\n",reco[1][0]);
	else printf("%d\n",reco[1][1]);
}
/*
	c[i][1/0]:做到了第i个点,第i个点选或不选。
	首先要使乘积最大,在能选的点里尽可能多的选一定不劣。
	c[i][0]=sigma(max(c[son][1],c[son][0]));
	c[i][1]=sigma(c[son][0])
*/

  另外,据题解说,由于数据是随的,不会有精度问题,将\(eps\)删掉也不会有问题。
  个人推测可能是因为数据随机会导致乘积出来即使挂\(log\)差别也会比较大(>1),就不会有精度问题了。

上一篇:42.出书最多


下一篇:LeetCode 42. 接雨水