D. The Omnipotent Monster Killer

D. The Omnipotent Monster Killer

You, the monster killer, want to kill a group of monsters. The monsters are on a tree with n vertices. On vertex with number i (1in), there is a monster with ai attack points. You want to battle with monsters for 10100 rounds.

In each round, the following happens in order:

  1. All living monsters attack you. Your health decreases by the sum of attack points of all living monsters.
  2. You select some (possibly all or none) monsters and kill them. After being killed, the monster will not be able to do any attacks in the future.

There is a restriction: in one round, you cannot kill two monsters that are directly connected by an edge.

If you choose what monsters to attack optimally, what is the smallest health decrement you can have after all rounds?

Input

Each test contains multiple test cases. The first line contains the number of test cases t (1t104). Description of the test cases follows.

The first line of each test case contains an integer n (1n3105).

The second line of each test case contains n integers a1,,an (1ai1012).

The following n1 lines each contain two integers x,y (1x,yn), denoting an edge on the tree connecting vertex x and y.

It is guaranteed that the sum of n over all test cases does not exceed 3105.

Output

For each test case, print one integer: the minimum possible health decrement.

Example

input

3
1
1000000000000
5
47 15 32 29 23
1 2
1 3
2 4
2 5
7
8 10 2 3 5 7 4
1 2
1 4
3 2
5 3
6 2
7 5

output

1000000000000
193
57

Note

In the first test case, an optimal sequence of operations would be:

  • In the first round: first, receive the attack from the monster on vertex 1, so your health decreases by 1012. Then kill the monster on vertex 1.
  • In the second round to the 10100-th round: all monsters have been killed, so nothing happens.

The total health decrement is 1012.

In the second test case, an optimal sequence of operations would be:

  • In the first round: first, receive the attack from the monster on vertex 1,2,3,4,5, so your health decreases by 47+15+32+29+23=146. Then kill the monsters on vertex 1,4,5.
  • In the second round: first, receive the attack from the monster on vertex 2,3, so your health decreases by 15+32=47. Then kill the monsters on vertex 2,3.
  • In the third round to the 10100-th round: all monsters have been killed, so nothing happens.

The total health decrement is 193.

In the third test case, an optimal sequence of operations would be:

  • In the first round: first, receive the attack from the monster on vertex 1,2,3,4,5,6,7, so your health decreases by 8+10+2+3+5+7+4=39. Then kill the monsters on vertex 1,3,6,7.
  • In the second round: first, receive the attack from the monster on vertex 2,4,5, so your health decreases by 10+3+5=18. Then kill the monsters on vertex 2,4,5.
  • In the third round to the 10100-th round: all monsters have been killed, so nothing happens.

The total health decrement is 57.

 

解题思路

  不猜的话很难想到做法,就是最多操作 O(logn) 轮就能把所有怪物消灭。在这基础上就可以 dp 求解答案了。

  为了方便这里固定节点 1 为树根。定义 f(u,i) 表示消灭以 u 为根的子树且 u 在第 i 轮被消灭所造成的最小代价,状态转移方程就是 f(u,i)=iau+vson(u)minji{f(v,j)}。其中为了方便这里定义 ij 的取值范围是 120(至少取到 19)。整个 dp 的时间复杂度是 O(nlog2n)

  在进行状态转移时,如果提前预处理出 li=min1ji{f(u,i)}ri=minij20{f(u,i)},那么状态转移方程就变成 f(u,i)=iau+vson(u)min{li1,ri+1},时间复杂度降到 O(nlogn)

  最后大概解释一下官方题解中关于最多操作 logn+1 轮就能把所有怪物消灭的证明。首先有一个贪心的结论,定义 bu 表示在最优解中节点 u 是在哪轮被消灭的,假设已知其每个儿子 vbv,那么一定有 bu=mexvson(u){bv}。在保证 bubv 的前提下,当然是希望节点 u 越早被消灭。

  现在假设节点 ubu=x 最大,并将 u 定为整棵树的根节点,由上面的结论知道 u 的所有儿子的 bv 必定包含了 1x1 的所有取值。定义 f(x) 表示根节点在第 x 轮被消灭的树中,至少包含的节点数量。那么有 f(x)1+i=1x1f(i)=2x2(1+f(1))=2x1f(x) 最大取 3105,因此反推得到 xlog3105+1=19

  AC 代码如下,时间复杂度为 O(nlogn)

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 3e5 + 5, M = N * 2;

LL a[N];
int h[N], e[M], ne[M], idx;
LL f[N][25], l[25], r[25];

void add(int u, int v) {
    e[idx] = v, ne[idx] = h[u], h[u] = idx++;
}

void dfs(int u, int p) {
    for (int i = 1; i <= 20; i++) {
        f[u][i] = i * a[u];
    }
    for (int i = h[u]; i != -1; i = ne[i]) {
        int v = e[i];
        if (v == p) continue;
        dfs(v, u);
        l[0] = r[21] = 1e18;
        for (int i = 1; i <= 20; i++) {
            l[i] = min(l[i - 1], f[v][i]);
        }
        for (int i = 20; i; i--) {
            r[i] = min(r[i + 1], f[v][i]);
        }
        for (int i = 1; i <= 20; i++) {
            f[u][i] += min(l[i - 1], r[i + 1]);
        }
    }
}

void solve() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%lld", a + i);
    }
    idx = 0;
    memset(h, -1, n + 1 << 2);
    for (int i = 0; i < n - 1; i++) {
        int u, v;
        scanf("%d %d", &u, &v);
        add(u, v), add(v, u);
    }
    dfs(1, 0);
    LL ret = 1e18;
    for (int i = 1; i <= 20; i++) {
        ret = min(ret, f[1][i]);
    }
    printf("%lld\n", ret);
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        solve();
    }
    
    return 0;
}

 

参考资料

  Editorial of Codeforces Round 958 (Div. 2):https://codeforces.com/blog/entry/131567

posted @   onlyblues  阅读(54)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示