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),就不会有精度问题了。
标签:乘积,reco,NOIP,read,ll,42,int,模拟,log 来源: https://www.cnblogs.com/Geek-Kay/p/15153819.html