P6082 [JSOI2015]salesman
题面
某售货员小\(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\n%s\n", f[1],
g[1] ? "solution is unique" : "solution is not unique");
return 0;
}