题目传送门(内部题59)
输入格式
第一行一个数$n$表示点数。
第二行$n$个数$A_i$。
接下来$n−1$行,每行两个数$u,v$表示$u$和$v$有边直接相连。
输出格式
一个数表示最小花费的起点。
样例
样例输入:
5
2 2 1 2 2
1 2
2 3
3 4
4 5
样例输出:
3
数据范围与提示
对于$10\%$的数据,保证$nleqslant 50$。
对于$30\%$的数据,保证$nleqslant 500$。
对于$50\%$的数据,保证$nleqslant 5,000$。
对于$70\%$的数据,保证$nleqslant 1 imes {10}^5$。
对于$100\%$的数据,保证$nleqslant 1 imes {10}^6$,$A_i$在$[1,1 imes {10}^9]$内随机生成。
题解
又是一道假的期望,还好我及时的看了出来……
我们设$g[i]$表示点$i$的子树对其的贡献,设$f[i]$表示点$i$的非子树对其的贡献。
显然子树贡献很好求,即为:
$$g[u]=sum limits_{v}frac{g[v]}{son[u]}+a[u]$$
现在考虑非子树的,即为:
$$f[u]=frac{(g[fa]+(g[fa]-a[fa]) imes son[u]-g[u])}{son[fa]}+a[fa]$$
那么,每个点的答案即为:
$$frac{(f[u]+(g[u]-a[u]) imes son[i])}{(son[i]+1)}+a[i]$$
题目稍卡常(考试的时候把常数写的稍大,被卡掉了$10$分……)
时间复杂度:$Theta(n)$。
期望得分:$100$分。
实际得分:$100$分。
代码时刻
#include<bits/stdc++.h>
using namespace std;
struct rec{int nxt,to;}e[2000001];
int head[1000001],cnt;
int n;
double a[1000001];
bool vis[1000001];
double du[1000001];
double dp[2][1000001];
pair<int,double> ans;
void add(int x,int y)
{
e[++cnt].nxt=head[x];
e[cnt].to=y;
head[x]=cnt;
}
void dfs(int x)
{
vis[x]=1;
dp[0][x]=a[x];
for(int i=head[x];i;i=e[i].nxt)
if(!vis[e[i].to])
{
dfs(e[i].to);
dp[0][x]+=dp[0][e[i].to]/du[x];
}
}
void DP(int x,int fa)
{
dp[1][x]=(dp[1][fa]+(dp[0][fa]-a[fa])*du[fa]-dp[0][x])/du[fa]+a[fa];
double flag=(dp[1][x]+(dp[0][x]-a[x])*du[x])/(du[x]+1)+a[x];
if(fabs(flag-ans.second)<1e-8)ans.first=min(x,ans.first);
if(ans.second-flag>1e-8)ans=make_pair(x,flag);
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa)DP(e[i].to,x);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lf",&a[i]);
du[i]=-1;
}
du[1]=0;
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
du[x]++;du[y]++;
}
dfs(1);
ans=make_pair(1,dp[0][1]);
for(int i=head[1];i;i=e[i].nxt)
DP(e[i].to,1);
printf("%d",ans.first);
return 0;
}
rp++