有一棵树,必须先走父亲再走儿子,但是不一定要走完一棵子树再走另一棵子树。每个点有两个权值 a i , b i a_i,b_i ai,bi。
现在你可以拿一个初始权值 T T T,按上述方法遍历这棵树,在经过一个节点的时候先减去 a i a_i ai,然后加上 b i b_i bi,要求任意时刻权值大于等于0。
求最小的初始权值。
先考虑没有父亲限制,每个点随时都可以取的情况。
在这道题里节点可以按照 a i a_i ai和 b i b_i bi的一些关系确定谁先取会更优。
首先显然 a i ≤ b i a_i\le b_i ai≤bi的肯定优于 a i > b i a_i>b_i ai>bi的。
考虑两个 a i ≤ b i , a j ≤ b j , a i < a j a_i\le b_i,a_j\le b_j,a_i
然后考虑两个 a i > b i , a j > b j a_i>b_i,a_j>b_j ai>bi,aj>bj,我们应该比较 v i = max ( a i , a i + a j − b i ) v_i=\max(a_i,a_i+a_j-b_i) vi=max(ai,ai+aj−bi)和 v j = max ( a j , a i + a j − b j ) v_j=\max(a_j,a_i+a_j-b_j) vj=max(aj,ai+aj−bj)的大小,谁的 v v v小谁适合放前面。
其实这时候做题已经OK了,但是结论好像还可以再简单一点,所以再做一些没有太大必要的工作。。。
大力分类讨论取 max \max max的情况:
( a i ≠ a i + a j − b i a_i \neq a_i+a_j-b_i ai=ai+aj−bi所以等于的情况不可能存在,在下面为了避免麻烦没有讨论进去。)
由上可知 a i > b i , a j > b j a_i>b_i,a_j>b_j ai>bi,aj>bj时 b i > b j b_i>b_j bi>bj是 i i i更优的充分条件。
所以只要比较 b b b就行了。真是优美。。。
现在对于任意两个节点,我们已经可以方便的判断他们的优先级大小关系了。然后考虑父亲对儿子的限制。
假如父亲的优先级小于儿子,那么父亲取完儿子必然紧接着要取,所以把儿子并到父亲上面,用并查集维护。最后剩下的就是一棵父亲优先级大于儿子的树,那么把所有节点丢进堆里,一个一个取出来就好了。
好题。
#include
using namespace std;
const int N = 1e5 + 10;
int n, fa[N], faz[N];
vector<int> to[N];
struct node{
long long a, b;
int id;
node(){}
node(long long _a, long long _b, int _id){
a = _a; b = _b; id = _id;
}
bool operator < (const node &u)const{
if (a <= b && u.a <= u.b) return a > u.a;
else if (a <= b && u.a > u.b) return false;
else if (a > b && u.a <= u.b) return true;
else if (a > b && u.a > u.b) return b < u.b;
}
bool operator != (const node &u)const{
return a != u.a || b != u.b;
}
}a[N], ans;
priority_queue<node> que;
bool vis[N];
bool cmp(int x, int y)
{
return a[x] < a[y];
}
int Find(int x)
{
if (fa[x] == x) return x;
return fa[x] = Find(fa[x]);
}
void merge(node &x, node y)
{
long long a, b;
a = max(x.a, x.a+y.a-x.b);
b = a + (-x.a+x.b-y.a+y.b);
x = node(a, b, x.id);
}
void dfs(int u, int _faz)
{
fa[u] = u;
faz[u] = _faz;
for (int i = 0, sz = to[u].size(); i < sz; ++ i)
if (to[u][i] != _faz)
dfs(to[u][i], u);
}
int main()
{
int T; scanf("%d", &T);
for (; T--; ){
scanf("%d", &n);
for (int i = 2; i <= n; ++ i){
scanf("%lld%lld", &a[i].a, &a[i].b);
a[i].id = i;
que.push(a[i]);
}
for (int i = 1; i <= n; ++ i)
to[i].clear();
for (int i = 2; i <= n; ++ i){
int x, y;
scanf("%d%d", &x, &y);
to[x].push_back(y);
to[y].push_back(x);
}
memset(vis, 0, sizeof(vis));
dfs(1, 0); vis[1] = 1;
ans = node(0, 0, 0);
while (!que.empty()){
node now = que.top(); que.pop();
if (now != a[now.id] || vis[now.id]) continue;
int real_fa = Find(faz[now.id]);
if (!vis[real_fa]){
merge(a[real_fa], now);
fa[now.id] = real_fa;
que.push(a[real_fa]);
}
else{
vis[now.id] = 1;
merge(ans, now);
}
}
printf("%lld\n", ans.a);
}
return 0;
}