题面
某售货员小(T) 要到若干城镇去推销商品,由于该地区是交通不便的山区,任意两个城镇 之间都只有唯一的可能经过其它城镇的路线。 小(T) 可以准确地估计出在每个城镇停留的净收 益。这些净收益可能是负数,即推销商品的利润抵不上花费。由于交通不便,小TT 经过每个城镇都需要停留,在每个城镇的停留次数与在该地的净收益无关,因为很多费用不是计次收取的,而每个城镇对小(T)的商品需求也是相对固定的,停留一次后就饱和了。每个城镇为了强化治安,对外地人的最多停留次数有严格的规定。请你帮小(T) 设计一个收益最大的巡回方案,即从家乡出发,在经过的每个城镇停留,最后回到家乡的旅行方案。你的程序只需输出最大收益,以及最优方案是否唯一。方案并不包括路线的细节,方案相同的标准是选择经过并停留的城镇是否相同。因为取消巡回也是一种方案,因此最大收益不会是负数。小(T)在家乡净收益是零,因为在家乡是本地人,家乡对小(T) 当然没有停留次数的限制。
Input
输入的第一行是一个正整数(n)((5<=n<=100000)),表示城镇数目。城镇以(1)到(n)的数命名。小(T) 的家乡命 名为(1)。
第二行和第三行都包含以空格隔开的(n−1)个整数,第二行的第ii个数表示在城镇(i+1)停留的净收益。
第三行的第ii个数表示城镇(i+1)规定的最大停留次数。所有的最大
停留次数都不小于(2)。
接下来的(n−1)行每行两个(1)到(n)的正整数(x,y),之间以一个空格隔开,表示(x,y)之间有一条不经过其它城镇的双向道路。输入数据保证所有城镇是连通的。
Output
输出有两行,第一行包含一个自然数,表示巡回旅行的最大收益。
如果该方案唯一,在 第二行输出“solution is unique”,否则在第二行输出“solution is not unique”。
思路
-
树形dp的入门题.(确定是省选的题?)
-
注意到次数限制(t)其实就是到达该点后,最多再进入它的(t−1)颗子树.
-
令(f[i])表示从(i)节点向下方走,最后回到(i)的最大收益.
-
令(g[i])表示取得(f[i])这个最大收益的方案是否唯一.
-
考虑状态转移,若记每个点的收益为(w),停留次数为(t).
-
则(f[i])就为(w[i])加上最多(t−1)个子树的收益.将儿子按照(f)排序即可.
-
(g[i])
在满足以下3中情况中任意一种时为
- 某个取得的儿子(f)值为(0).(我们可以选择不取它).
- 某个取得的儿子(g)值为(0).(我们在这颗子树中有不同的路径)
- 下个未选的儿子(如果有)和最后选择的儿子(f)值相同.(可以替换).
-
其他时候(g[i])均为(1).
-
答案即为(f[1],g[1]).
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LoveLive;
const int inf = 0x7fffffff;
inline int read()
{
int x=0;
int f=1;
char ch;
ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10,x=x+ch-'0';
ch=getchar();
}
return x*f;
}
const int MAXN = 2e5 + 10;
int t[MAXN];
int head[MAXN], idx;
int ver[MAXN << 1], ne[MAXN << 1];
inline void add(int u, int v) {
ver[idx] = v;
ne[idx] = head[u];
head[u] = idx;
idx++;
}
int n;
int w[MAXN];
int f[MAXN], g[MAXN];
bool cmp(int a, int b) { return f[a] > f[b]; }
void dfs(int u, int father) {
f[u] = w[u];
g[u] = 1;
vector<int> son;
for (int i = head[u]; i != -1; i = ne[i]) {
int j = ver[i];
if (j == father) continue;
dfs(j, u);
son.push_back(j);
}
sort(son.begin(), son.end(), cmp);
int siz = son.size();
int limit = min(siz, t[u]);
for (int i = 0; i < limit; i++) {
if (f[son[i]] < 0) continue;
f[u] += f[son[i]];
if (f[son[i]] == 0) g[u] = 0;
if (g[son[i]] == 0) g[u] = 0;
}
}
int main() {
memset(head, -1, sizeof(head));
n = read();
for (int i = 2; i <= n; ++i) w[i] = read();
for (int i = 2; i <= n; ++i)
t[i] = read(), --t[i]; // t[i]表示i最多能选取多少个子树
for (int i = 1; i < n; ++i) {
int u = read(), v = read();
add(u, v);
add(v, u);
}
w[1] = 0;
t[1] = inf;
dfs(1, 0);
printf("%d
%s
", f[1],
g[1] ? "solution is unique" : "solution is not unique");
return 0;
}