题目
某售货员小T要到若干城镇去推销商品,由于该地区是交通不便的山区,任意两个城镇之间都只有唯一的可能经过其它城镇的路线。小T可以准确地估计出在每个城镇停留的净收益。这些净收益可能是负数,即推销商品的利润抵不上花费。由于交通不便,小T经过每个城镇都需要停留,在每个城镇的停留次数与在该地的净收益无关,因为很多费用不是计次收取的,而每个城镇对小T的商品需求也是相对固定的,停留一次后就饱和了。每个城镇为了强化治安,对外地人的最多停留次数有严格的规定。请你帮小T设计一个收益最大的巡回方案,即从家乡出发,在经过的每个城镇停留,最后回到家乡的旅行方案。你的程序只需输出最大收益,以及最优方案是否唯一。方案并不包括路线的细节,方案相同的标准是选择经过并停留的城镇是否相同。因为取消巡回也是一种方案,因此最大收益不会是负数。小T在家乡净收益是零,因为在家乡是本地人,家乡对小T当然没有停留次数的限制。
输入格式
输入的第一行是一个正整数(n(5<=n<=100000)),表示城镇数目。城镇以(1)到(n)的数命名。小T的家乡命名为(1)。第二行和第三行都包含以空格隔开的(n-1)个整数,第二行的第(i)个数表示在城镇(i+1)停留的净收益。第三行的第(i)个数表示城镇(i+1)规定的最大停留次数。所有的最大停留次数都不小于(2)。接下来的(n-1)行每行两个1到(n)的正整数(x),(y),之间以一个空格隔开,表示(x),(y)之间有一条不经过其它城镇的双向道路。输入数据保证所有城镇是连通的。
输出格式
输出有两行,第一行包含一个自然数,表示巡回旅行的最大收益。如果该方案唯一,在第二行输出"solution is unique",否则在第二行输出"solution is not unique"。
输入样例
9
-3 -4 2 4 -2 3 4 6
4 4 2 2 2 2 2 2
1 2
1 3
1 4
2 5
2 6
3 7
4 8
4 9
输出样例
9
solution is unique
样例解释
最佳路线包括城镇 1,2, 4, 5, 9
题解
任意两个城镇之间都只有唯一的可能经过其它城镇的路线,说明这一定是一棵树
第一问
这道题明显是树形动规,但加了一个限制条件,就是最大停留次数,联想树形动规时的形式,这个最大停留次数其实和能访问的子树个数有关系.
上图表示了一个DFS过程,其中2,3,4,5,6号都是子树,若将这些子树看成一个点,则DFS过程中经过的节点为
[1] 2 [1] 2 [1] 4 [1] 5 [1] 6 [1]
注意其中的根节点1,出现了6次,本题中就是停留了6次,而1号节点有5棵子树,可以发现,若i节点最大停留次数为(limit[x]),则DFS中最多能访问(limit[x]-1)棵子树
这些子树中对根节点dp的贡献不同,我们当然要选择其中最大的,所以排一下序,选其中前(limit[x]-1)棵子树来更新根节点的dp值.
注意,(limit[x]-1)也存在大于子树个数的情况,所以实际操作的时候要取(limit[x])和子树个数的最小值作为更新根节点的子树数量,由于净收益可能是负数,所以更新的时候发现是负数立刻停止即可.
所以遍历子树(已排序)时候,条件为
soni < min(limit[root] - 1, sontot) && dp[sonn[soni + 1]] >= 0
其中,soni
为当前循环遍历的子树时的循环变量(注意不时子树根节点编号),root
为根节点编号,sontot
为子树的数量,sonn
数组保存子树的根节点编号,sonn[soni+1]
为这次循环的子树编号(因为soni在循环内自增1)
还有一个条件
家乡对小T当然没有停留次数的限制
也就是整棵树的根节点无限制次数,那么只需要在DFS之前将根节点的limit
值赋值为无限大即可
第二问
显然,根节点方案是否唯一首先要看其子树,如果有任意一棵更新了根节点dp值得子树的方案不唯一,根节点的方案显然也不唯一.
除此之外,还有存在子树方案唯一但根节点选取子树的方案不唯一的情况.
遍历子树的时候,将 子树方案是否唯一的值 和 根节点选取子树的方案是否唯一 的值进行或运算(只要有一个为真,结果就为真),得到的结果就是这棵树的方案是否唯一,最后输出即可.
那么怎么判断根节点选取子树的方案是否唯一呢,有两种情况:
- 相同值引起的不唯一
假设排好序后的子树dp值为10 9 8 7 6 6 5 4 3 2 1
,而你只能选5个(limit
值为6),显然你选择的是10 9 8 7 6
,但是仔细观察,你会发现还有一个相同的6,那么我能不能抛弃第一个6选择第二个6呢?当然可以,那么,这就有了两种选择办法([10] [9] [8] [7] [6] 6 5 4 3 2 1
和[10] [9] [8] [7] 6 [6] 5 4 3 2 1
),这种情况不需要注意limit
值的限制,因为没有多选择一个,是抛弃一个再选一个,用来更新的子树数量没有改变.
- dp值为0引起的不唯一
假设排好序后的子树dp值为10 9 8 7 6 0
,而你只能选5个(limit值为6),你可以选择10 9 8 7 6
,也可以选择10 9 8 7 6 0
,这两种方案更新的值都是一样的,这种情况需要注意limit
值的限制,即使limit
值为0,如果选择了,也会多一次停留,虽然数据有点水,亲测加不加limit值的限制都能A.
出现这两种情况时,直接将这棵树方案是否唯一的赋值为真即可
代码
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 100000;
struct edge {
int i, next;
} edges[2 * N + 5];
int head[N + 5], tot, n, w[N + 5], limit[N + 5], dp[N + 5], ansn[N + 5],sonn[N + 5];
void add(int u, int v) {
edges[++tot].i = v;
edges[tot].next = head[u];
head[u] = tot;
}
bool cmp(int a, int b) { return dp[a] > dp[b]; }
void dfs(int root, int f) {
dp[root] = w[root];
int sontot = 0, soni = 0;
for (int i = head[root]; i; i = edges[i].next)
if (edges[i].i != f) dfs(edges[i].i, root);
for (int i = head[root]; i; i = edges[i].next)
if (edges[i].i != f) sonn[++sontot] = edges[i].i;
sort(sonn + 1, sonn + 1 + sontot, cmp);
while (soni < min(limit[root] - 1, sontot) && dp[sonn[soni + 1]] >= 0)
dp[root] += dp[sonn[++soni]], ansn[root] |= ansn[sonn[soni]];//按位或
if (soni < sontot && soni > 0 && dp[sonn[soni]] == dp[sonn[soni + 1]] || dp[sonn[soni]] == 0 && soni > 0 && soni <= limit[root] - 1)//两种情况,注意边界
ansn[root] = 1;
}
int main() {
scanf("%d", &n);
for (int i = 1; i < n; i++) scanf("%d", &w[i + 1]);
for (int i = 1; i < n; i++) scanf("%d", &limit[i + 1]);
for (int i = 1; i < n; i++) {
int u, v;
scanf("%d%d", &u, &v);
add(u, v);
add(v, u);
}
limit[1] = n + 1;//在家乡没有停留限制
dfs(1, 0);
printf("%d
%s", dp[1], ansn[1] ? "solution is not unique" : "solution is unique");
return 0;
}