LibreOJ L6210 「美团 CodeM 决赛」tree

Posted lhzQAQ 太菜了

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LibreOJ L6210 「美团 CodeM 决赛」tree相关的知识,希望对你有一定的参考价值。

难度:$\\texttt?$

链接
难度:\\(\\texttt?\\)


有一颗 \\(n\\) 个点的树,每个点有权值 \\(x_i\\),定义一条简单路径的权值为 \\(f(a_1\\to a_2\\to...\\to a_k)=\\fracx_a_1\\times x_a_2\\times ...\\times x_a_kk\\),求最小的 \\(f(a_1\\to a_2\\to...\\to a_k)\\) 值。

数据范围:\\(1\\le n\\le500000, 1\\le x_i\\le 10^7\\ \\texttt1s 512MB\\)


这题都做不出来我去送外卖吧.jpg


凭直觉也能想到 \\(1\\le x_i\\le 10^7\\) 肯定是唬人的,考虑只有哪些 \\(x_i\\) 能对答案产生贡献。

首先能想到的就是\\(x_i=1\\),分子不变,分母 \\(+1\\),答案肯定会小。
然后就需要推式子了,设目前有连续 \\(z\\) 个数字 \\(p(p>1,z\\ge 1)\\),其左边有 \\(x\\)\\(1\\),右边有 \\(y\\)\\(1(1\\le x\\le y)\\),则这条路径权值为 \\(\\fracp^zx+y+z\\),而目前要比较的权值为 \\(\\frac1y\\),需使 \\(\\fracp^zx+y+z<\\frac1y\\)

  • 两边先同时 \\(\\times y\\times (x+y+z)\\)\\(y\\times p^z<x+y+z\\)
  • 因为 \\(x\\le y\\),不妨设 \\(x=y\\)\\(y\\times p^z<2y+z\\)
  • 两边同时减掉 \\(2y\\)\\(y\\times (p^z-2)<z\\)

可以发现只有 \\(p=2,z=1\\) 时成立。

那这道题就很明显了,\\(x_i=1\\) 可以随便选,\\(x_i=2\\) 至多选一个,树形 DP 即可。

\\(Max1_u\\) 为经过 \\(u\\) 点路径连续为 \\(x_i=1\\) 最多的点数,\\(Max2_u\\) 为经过 \\(u\\) 点路径上存在一个 \\(2\\) 其余的路径连续为 \\(x_i=1\\) 最多的点数(包括 \\(2\\))。

方程还是挺好推的:\\(Max1_u=\\max\\Max1_v\\+1,Max2_u=\\max\\Max2_v\\+1(x_u=1)Max2_u=\\max\\\\Max1_v\\+1(x_u=2)\\)
转移时统计答案即可。


# include <bits/stdc++.h>
using namespace std;
const int N = 500000;
vector <int> to [N + 10];
int x [N + 10];
int Max1 [N + 10], Max2 [N + 10];
int ans1, ans2;
void DFS (int u, int fa) 
	if (x [u] == 1) 
		Max1 [u] = 1;
	
	else if (x [u] == 2) 
		Max2 [u] = 1;
	
	for (int v : to [u]) 
		if (v == fa) 
			continue;
		
		DFS (v, u);
		ans1 = max (ans1, Max1 [u] + Max1 [v]);
		ans2 = max (ans2, max (Max1 [u] + Max2 [v], Max2 [u] + Max1 [v]));// 两段拼起来
		if (x [u] == 1) 
			Max1 [u] = max (Max1 [u], Max1 [v] + 1);
			Max2 [u] = max (Max2 [u], Max2 [v] + 1);
		
		else if (x [u] == 2) 
			Max2 [u] = max (Max2 [u], Max1 [v] + 1);
		
	
	return ;

const int A = 500000;
int main () 
	int n;
	scanf ("%d", & n);
	for (int i = 1; i < n; ++ i) 
		int x, y;
		scanf ("%d %d", & x, & y);
		to [x] .push_back (y);
		to [y] .push_back (x);
	
	int Min = A;
	for (int i = 1; i <= n; ++ i) 
		scanf ("%d", & x [i]);
		Min = min (Min, x [i]);
	
	if (Min > 1) 
		printf ("%d/1", Min);
		return 0;
	
	DFS (1, 0);
	if ((ans1 << 1) > ans2) // 注意这里实际上是 1 / ans1 和 2 / ans2 分母大的对应的分数更小
		printf ("1/%d", ans1);
	
	else 
		if (ans2 & 1) 
			printf ("2/%d", ans2);
		
		else 
			printf ("1/%d", ans2 >> 1);
		
	
	return 0;

以上是关于LibreOJ L6210 「美团 CodeM 决赛」tree的主要内容,如果未能解决你的问题,请参考以下文章

LibreOJ 6048 美团 CodeM 资格赛 跳格子

「美团 CodeM 资格赛」试题泛做

「题解」「美团 CodeM 资格赛」跳格子

美团CodeM资格赛第二题

「美团 CodeM 复赛」城市网络

CodeM美团点评编程竞赛资格赛题