其他分享
首页 > 其他分享> > [COCI2016-2017#1] Mag 题解

[COCI2016-2017#1] Mag 题解

作者:互联网

[COCI2016-2017#1] Mag 题解

题目描述

你将获得一棵由无向边连接的树。树上每个节点都有一个魔力值。

我们定义,一条路径的魔力值为路径上所有节点魔力值的乘积除以路径上的节点数。

例如,若一条路径包含两个魔力值分别为\(3,5\)的节点,则这条路径的魔力值为 \(3\times 5/2=7.5\)。

请你计算,这棵树上魔力值最小的路径的魔力值。

输入格式

第一行一个整数\(n\),表示树共有\(n\)个节点,编号为\(1\dots n\)。

接下来\(n-1\)行,每行两个整数\(a_i,b_i\),表示编号为\(a_i,b_i\)的两个节点由一条无向边连接。

接下来\(n\)行,每行一个整数\(x_i\),表示编号为\(i\)的节点的魔力值。

输出格式

一行,一个既约分数\(p/q\)。

输入输出样例
输入 #1

2
1 2
3
4 

输出 #1

3/1 

输入 #2

5
1 2
2 4
1 3
5 2
2
1
1
1
3

输出 #2

1/2

说明/提示
【样例解释】
样例 1 解释

注意,路径可以只包含一个节点。

这棵树上魔力值最小的路径的包含节点\(1\),其魔力值为\(3/1\)。

样例 2 解释

这棵树上魔力值最小的路径的包含节点\(2,4\),其魔力值为\(1\times 1/2=1/2\)。

数据规模与约定
对于\(100\%\)的数据,\(1\le n\le 10^6\),\(1\le a_i,b_i\le n\),\(1\le x_i\le 10^9\)。

数据保证,\(p,q\)不会超过\(10^{18}\)。

说明
题目译自 COCI2016-2017 CONTEST #1 T4 Mag。

思路

首先来证明一点。满足题意的最优解一定为由全部是1,或仅含有1个2的链来组成的(当且仅当2两边的)。
   a     b
——————2——————
1 1 1 1 1 1 1 2 1 1 1 1 1 1 1
证明:

有了上述的证明,就可以进行树形DP了。就称只含有1的链为1链,其中含了一的1链为2链。

设\(dp[i][0|1][0|1]\)的第一维表示是哪一个节点的状态。对应的价值最小。第二维表示该节点参与的链中是否含有2这个点,若含有2,则该维度对应值为0,否则为1。第三维表示分子分母,1为分子,0为分母。

\(sec[0|1][0|1]\)对应的是次小值。其中的第一维与\(dp[i]\)的第二维意义相同,第二维与其第三维相同。

\(id[0|1][0|1]\)的第一维表示该节点参与的链中是否含有2这个点,若含有2,则该维度对应值为0,否则为1。第二维表示最小一次对应次小与最小。

先来说说如何状态转移。

如下面的代码所示(主体部分)。

for(int i = 0; i < SIZ; i++) {
	int next = v[now][i];
	if(next == fa)
		continue;
	DP(next, now);
	if(val[now] == 1) {
		if(val[next] > 2)
			continue;
		double son1 = (dp[next][1][1] * 1.0) / ((dp[next][1][0] + 1) * 1.0);
		double son2 = (dp[next][0][1] * 1.0) / ((dp[next][0][0] + 1) * 1.0);
		double self1 = (dp[now][1][1] * 1.0) / (dp[now][1][0] * 1.0);
		double self2 = (dp[now][0][1] * 1.0) / (dp[now][0][0] * 1.0);
		double num1 = (sec[1][1] * 1.0) / (sec[1][0] * 1.0);
		double num2 = (sec[0][1] * 1.0) / (sec[0][0] * 1.0);
		if(son1 < self1) {
			sec[1][1] = dp[now][1][1];
			sec[1][0] = dp[now][1][0];
			id[1][0] = id[1][1];
			dp[now][1][1] = dp[next][1][1];
			dp[now][1][0] = dp[next][1][0] + 1;
			id[1][1] = next;
		}
		else if(son1 < num1) {
			id[1][0] = next;
			sec[1][1] = dp[next][1][1];
			sec[1][0] = dp[next][1][0] + 1;
		}
		if(son2 < self2) {
			sec[0][1] = dp[now][0][1];
			sec[0][0] = dp[now][0][0];
			id[0][0] = id[0][1];
			dp[now][0][1] = dp[next][0][1];
			dp[now][0][0] = dp[next][0][0] + 1;
			id[0][1] = next;
		}
		else if(son2 < num2) {
			sec[0][1] = dp[next][0][1];
			sec[0][0] = dp[next][0][0] + 1;
			id[0][0] = next;
		}
	}
	else if(val[now] == 2) {
		if(val[next] != 1)
			continue;
		double son = (dp[next][1][1] * 2.0) / ((dp[next][1][0] + 1) * 1.0);
		double self = (dp[now][0][1] * 1.0) / (dp[now][0][0] * 1.0);
		double num = (sec[0][1] * 1.0) / (sec[0][0] * 1.0);
		if(son < self) {
			sec[0][1] = dp[now][0][1];
			sec[0][0] = dp[now][0][0];
			id[0][0] = id[0][1];
			dp[now][0][1] = dp[next][1][1] * 2;
			dp[now][0][0] = dp[next][1][0] + 1;
			id[0][1] = next;
		}
		else if(son < num) {
			sec[0][1] = dp[next][1][1] * 2;
			sec[0][0] = dp[next][1][0] + 1;
			id[0][0] = next;
		}
	}
}

情况一

设当前正在遍历的节点为now,对于now的每一个子节点next,若有now的权值为1,则有:

若next的权值大于2,则没有资格更新自己的父节点。需要先判断是否有资格更新父节点的值。

if(val[next] > 2)
	continue;

又有几种情况:

if(son1 < self1) {
	sec[1][1] = dp[now][1][1];
	sec[1][0] = dp[now][1][0];
	id[1][0] = id[1][1];
	dp[now][1][1] = dp[next][1][1];
	dp[now][1][0] = dp[next][1][0] + 1;
	id[1][1] = next;
}
else if(son1 < num1) {
	id[1][0] = next;
	sec[1][1] = dp[next][1][1];
	sec[1][0] = dp[next][1][0] + 1;
}
if(son2 < self2) {
	sec[0][1] = dp[now][0][1];
	sec[0][0] = dp[now][0][0];
	id[0][0] = id[0][1];
	dp[now][0][1] = dp[next][0][1];
	dp[now][0][0] = dp[next][0][0] + 1;
	id[0][1] = next;
}
else if(son2 < num2) {
	sec[0][1] = dp[next][0][1];
	sec[0][0] = dp[next][0][0] + 1;
	id[0][0] = next;
}

情况二

设当前正在遍历的节点为now,对于now的每一个子节点next,若有now的权值为2。因为这条链中必会有2,所以就不需要考虑更新1链的价值。则有:

若当前子节点的子节点的权值不为1,则也没有资格更新父节点。

if(val[next] != 1)
	continue;

更新方式与上述相同。

但最小2链只能由儿子的1链来更新,因为一条2链中只能含有1个2,而当前节点就是2。

if(son < self) {
	sec[0][1] = dp[now][0][1];
	sec[0][0] = dp[now][0][0];
	id[0][0] = id[0][1];
	dp[now][0][1] = dp[next][1][1] * 2;
	dp[now][0][0] = dp[next][1][0] + 1;
	id[0][1] = next;
}
else if(son < num) {
	sec[0][1] = dp[next][1][1] * 2;
	sec[0][0] = dp[next][1][0] + 1;
	id[0][0] = next;
}

现在来更新答案

又分了几种情况

double Num = (dp[now][0][1] * 1.0) / (dp[now][0][0] * 1.0);
if(Num < ans) {
	ans = Num;
	ans1 = dp[now][0][1];
	ans2 = dp[now][0][0];
}
Num = (dp[now][1][1] * 1.0) / (dp[now][1][0] * 1.0);
if(Num < ans) {
	ans = Num;
	ans1 = dp[now][1][1];
	ans2 = dp[now][1][0];
}
if(id[1][1] != id[0][1]) {
	Num = ((dp[now][0][1] * dp[now][1][1]) * 1.0) / ((dp[now][0][0] + dp[now][1][0] - 1) * 1.0);
	if(Num < ans) {
		ans = Num;
		ans1 = dp[now][0][1] * dp[now][1][1];
		ans2 = dp[now][0][0] + dp[now][1][0] - 1;
	}
}
else {
	if(id[1][0] != id[0][1]) {
		Num = ((dp[now][0][1] * sec[1][1]) * 1.0) / ((dp[now][0][0] + sec[1][0] - 1) * 1.0);
		if(Num < ans) {
			ans = Num;
			ans1 = dp[now][0][1] * sec[1][1];
			ans2 = dp[now][0][0] + sec[1][0] - 1;
		}
	}
	if(id[0][0] != id[1][1]) {
		Num = ((sec[0][1] * dp[now][1][1]) * 1.0) / ((sec[0][0] + dp[now][1][0] - 1) * 1.0);
		if(Num < ans) {
			ans = Num;
			ans1 = sec[0][1] * dp[now][1][1];
			ans2 = sec[0][0] + dp[now][1][0] - 1;
		}
	}
}
Num = ((dp[now][1][1] * sec[1][1]) * 1.0) / ((dp[now][1][0] + sec[1][0] - 1) * 1.0);
if(Num < ans) {
	ans = Num;
	ans1 = dp[now][1][1] * sec[1][1];
	ans2 = dp[now][1][0] + sec[1][0] - 1;
}

输出

记得分子分母需要互质。

C++代码

#include <cstdio>
#include <vector>
using namespace std;
#define INF 1e8
#define Min(a, b) ((a) < (b) ? (a) : (b))
void Quick_Read(int &N) {
	N = 0;
	char c = getchar();
	int op = 1;
	while(c < '0' || c > '9') {
		if(c == '-')
			op = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9') {
		N = (N << 1) + (N << 3) + c - 48;
		c = getchar();
	}
	N *= op;
}
const int MAXN = 1e6 + 5;
bool flag;
vector<int> v[MAXN];
int down[MAXN], dp[MAXN][2][2];
int val[MAXN];
int n, minn;
double ans;
int ans1, ans2;
int GCD(int a, int b) {
	return b == 0 ? a : GCD(b, a % b);
}
void DP(int now, int fa) {
	int sec[2][2];
	int id[2][2];
	for(int i = 0; i <= 1; i++)
		for(int j = 0; j <= 1; j++)
			sec[i][j] = id[i][j] = INF;
	int SIZ = v[now].size();
	for(int i = 0; i < SIZ; i++) {
		int next = v[now][i];
		if(next == fa)
			continue;
		DP(next, now);
		if(val[now] == 1) {
			if(val[next] > 2)
				continue;
			double son1 = (dp[next][1][1] * 1.0) / ((dp[next][1][0] + 1) * 1.0);
			double son2 = (dp[next][0][1] * 1.0) / ((dp[next][0][0] + 1) * 1.0);
			double self1 = (dp[now][1][1] * 1.0) / (dp[now][1][0] * 1.0);
			double self2 = (dp[now][0][1] * 1.0) / (dp[now][0][0] * 1.0);
			double num1 = (sec[1][1] * 1.0) / (sec[1][0] * 1.0);
			double num2 = (sec[0][1] * 1.0) / (sec[0][0] * 1.0);
			if(son1 < self1) {
				sec[1][1] = dp[now][1][1];
				sec[1][0] = dp[now][1][0];
				id[1][0] = id[1][1];
				dp[now][1][1] = dp[next][1][1];
				dp[now][1][0] = dp[next][1][0] + 1;
				id[1][1] = next;
			}
			else if(son1 < num1) {
				id[1][0] = next;
				sec[1][1] = dp[next][1][1];
				sec[1][0] = dp[next][1][0] + 1;
			}
			if(son2 < self2) {
				sec[0][1] = dp[now][0][1];
				sec[0][0] = dp[now][0][0];
				id[0][0] = id[0][1];
				dp[now][0][1] = dp[next][0][1];
				dp[now][0][0] = dp[next][0][0] + 1;
				id[0][1] = next;
			}
			else if(son2 < num2) {
				sec[0][1] = dp[next][0][1];
				sec[0][0] = dp[next][0][0] + 1;
				id[0][0] = next;
			}
		}
		else if(val[now] == 2) {
			if(val[next] != 1)
				continue;
			double son = (dp[next][1][1] * 2.0) / ((dp[next][1][0] + 1) * 1.0);
			double self = (dp[now][0][1] * 1.0) / (dp[now][0][0] * 1.0);
			double num = (sec[0][1] * 1.0) / (sec[0][0] * 1.0);
			if(son < self) {
				sec[0][1] = dp[now][0][1];
				sec[0][0] = dp[now][0][0];
				id[0][0] = id[0][1];
				dp[now][0][1] = dp[next][1][1] * 2;
				dp[now][0][0] = dp[next][1][0] + 1;
				id[0][1] = next;
			}
			else if(son < num) {
				sec[0][1] = dp[next][1][1] * 2;
				sec[0][0] = dp[next][1][0] + 1;
				id[0][0] = next;
			}
		}
	}
	double Num = (dp[now][0][1] * 1.0) / (dp[now][0][0] * 1.0);
	if(Num < ans) {
		ans = Num;
		ans1 = dp[now][0][1];
		ans2 = dp[now][0][0];
	}
	Num = (dp[now][1][1] * 1.0) / (dp[now][1][0] * 1.0);
	if(Num < ans) {
		ans = Num;
		ans1 = dp[now][1][1];
		ans2 = dp[now][1][0];
	}
	if(id[1][1] != id[0][1]) {
		Num = ((dp[now][0][1] * dp[now][1][1]) * 1.0) / ((dp[now][0][0] + dp[now][1][0] - 1) * 1.0);
		if(Num < ans) {
			ans = Num;
			ans1 = dp[now][0][1] * dp[now][1][1];
			ans2 = dp[now][0][0] + dp[now][1][0] - 1;
		}
	}
	else {
		if(id[1][0] != id[0][1]) {
			Num = ((dp[now][0][1] * sec[1][1]) * 1.0) / ((dp[now][0][0] + sec[1][0] - 1) * 1.0);
			if(Num < ans) {
				ans = Num;
				ans1 = dp[now][0][1] * sec[1][1];
				ans2 = dp[now][0][0] + sec[1][0] - 1;
			}
		}
		if(id[0][0] != id[1][1]) {
			Num = ((sec[0][1] * dp[now][1][1]) * 1.0) / ((sec[0][0] + dp[now][1][0] - 1) * 1.0);
			if(Num < ans) {
				ans = Num;
				ans1 = sec[0][1] * dp[now][1][1];
				ans2 = sec[0][0] + dp[now][1][0] - 1;
			}
		}
	}
	Num = ((dp[now][1][1] * sec[1][1]) * 1.0) / ((dp[now][1][0] + sec[1][0] - 1) * 1.0);
	if(Num < ans) {
		ans = Num;
		ans1 = dp[now][1][1] * sec[1][1];
		ans2 = dp[now][1][0] + sec[1][0] - 1;
	}
}
void Read() {
	ans = INF;
	minn = INF;
	int A, B;
	Quick_Read(n);
	for(int i = 1; i < n; i++) {
		Quick_Read(A);
		Quick_Read(B);
		v[A].push_back(B);
		v[B].push_back(A);
	}
	for(int i = 1; i <= n; i++) {
		Quick_Read(val[i]);
		if(val[i] == 1) {
			flag = true;
		}
		minn = Min(minn, val[i]);
	}
	for(int i = 1; i <= n; i++) {
		dp[i][0][1] = dp[i][1][1] = INF;
		if(val[i] == 1) {
			dp[i][1][1] = 1;
			dp[i][0][1] = 1;
		}
		else if(val[i] == 2) {
			dp[i][1][1] = 2;
			dp[i][0][1] = 2;
		}
		dp[i][0][0] = dp[i][1][0] = 1;
	}
}
int main() {
	Read();
	if(!flag) {
		printf("%d/1", minn);
		return 0;
	}
	DP(1, -1);
	int gcd = GCD(ans1, ans2);
	ans1 /= gcd;
	ans2 /= gcd;
	printf("%d/%d", ans1, ans2);
	return 0;
}

标签:1.0,题解,next,sec,COCI2016,2017,now,id,dp
来源: https://www.cnblogs.com/C202202chenkelin/p/14035444.html