洛谷 U141397 !
题目背景
终于来到了最后一个电脑。你暗自嘲讽$$JZW$$最后一题想不出来名字只能用感叹号,心想下一套模拟赛要不要叫CXZL,走向了最后一台电脑。·
题目描述
你终于看见了被抓起来的$$JZW$$。看到了身为多项式之一的$$JZW$$的你十分喜悦,并和他一起合唱了《牵丝戏》和《九张机》,而要想解救他,你需要切掉这最后一道题。
在你的面前是一颗无根树,树上每个节点都有一个权值。我们定义一个路径的权值为:该路径上所有节点权值的乘积除以路径上的节点数。
例如,一条路径上包含两个权值分别为$$3,7$$,则该路径的权值为5
现在,你需要计算出这棵树上权值最小的路径的权值,才能救出$$JZW$$。
输入格式
从文件fxxk.infxx**k.i**n中读入数据。
第一行一个整数$$N$$,表示树共有$$N$$个节点
接下来$$N-1$$行,每行两个整数$$a_i,b_i$$,表示编号为$$a_i,b_i$$的两个节点有一条无向边连接
接下来$$N$$行,每行一个整数$$w_i$$,表示该节点$$i$$的权值
输出格式
输出到文件fxxk.outfxx**k.out中。
一行,一个既约分数,形如p/q,如3/1
题解:
2020.11.23模拟赛T4 25分场
觉得可以用点分治做。但是考场上一直没写出来。如果觉得点分治做法可行的小伙伴可以私信或留言联系我。咱俩一起研究。
在考场上发现一个性质:1越多贡献越多。而且,除了1之外,再往里加任何数都不优。
于是树形DP求了个最长的1链。最后假了,挂了好多分。
问题出在哪呢?
原来,2也是可以被放进这条链的。可以这样去想:现在有两条最长1链(分开的),只需要中间的一个2就可以把它们沟通起来。这样的答案有可能更优秀。
于是我们就可以用(f[i],g[i])分别统计以i为根的子树中,以i为一端且魔力值为1的最大长度、魔力值为2的最大长度。
当然,根据之前的思路,我们为了统计这些信息,还需要统计最大值、次大值。
具体的在代码中实现:
#include<cstdio>
using namespace std;
const int maxn=1e6+6;
struct Node
{
int next;
int to;
}edge[maxn<<1];
int head[maxn],cnt;
void add(int u,int v)
{
edge[cnt].next=head[u];
edge[cnt].to=v;
head[u]=cnt++;
}
int mag[maxn];
int f[maxn],g[maxn];
int p=1e9,q=1,mn=1e9;
void update(int x,int y)
{
if(p*y>q*x)
p=x,q=y;
return;
}
void dfs(int x,int fa)
{
int u1=0,u2=0,v1=0,v2=0;
for(int i=head[x];~i;i=edge[i].next)
{
int t=edge[i].to;
if(t!=fa)
{
dfs(t,x);
if(f[t]>f[u1]){
u2=u1,u1=t;
}
else if(f[t]>f[u2]){
u2=t;
}
if(g[t]>g[v1]){
v2=v1,v1=t;
}
else if(g[t]>g[v2]){
v2=t;
}
}
}
if(mag[x]==1)
{
f[x]=f[u1]+1;
update(1,f[u1]+f[u2]+1);
if(v1!=0)
{
g[x]=g[v1]+1;
if(u1!=v1)
update(2,f[u1]+g[v1]+1);
else
{
update(2,f[u2]+g[v1]+1);
if(v2!=0)
update(2,f[u1]+g[v2]+1);
}
}
}
else if(mag[x]==2)
{
g[x]=f[u1]+1;
update(2,f[u1]+f[u2]+1);
}
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i)
head[i]=-1;
for(int i=1;i<n;++i)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
for(int i=1;i<=n;++i)
{
scanf("%d",mag+i);
if(mag[i]<mn)
mn=mag[i];
}
if(mn>1)
{
printf("%d/1",mn);
return 0;
}
dfs(1,0);
printf("%d/%d",p,q);
return 0;
}