Description
有一棵 n n 个节点的树,每个点有一个怪物,打败该怪物消耗血量为 ai a i ,打败怪物后回血为 bi b i ,勇士从 1 1 节点出发开始打怪,只有当勇士血量不低于怪物血量时才能打败怪物,且只有打败父亲节点的怪物才能打当前节点的怪物,问勇士初始最少需要多少血量才能打败所有怪物
Input
第一行一整数 T T 表示用例组数,每组用例首先输入一整数 n n 表示点数,之后 n n 行每行输入两个整数 ai,bi a i , b i 表示打败第 i i 个怪物需要扣掉的血量和打败第 i i 个怪物可以回复的血量,最后 n−1 n − 1 行输入 n−1 n − 1 条树边
(1≤T≤2000,2≤n≤105,0≤ai,bi≤109,∑n≤106) ( 1 ≤ T ≤ 2000 , 2 ≤ n ≤ 10 5 , 0 ≤ a i , b i ≤ 10 9 , ∑ n ≤ 10 6 )
Output
输出勇士打败所有怪物需要的最少初始血量
Sample Input
1
4
2 6
5 4
6 2
1 2
2 3
3 4
Sample Output
3
Solution
先不考虑父亲的限制,那么必然首先选 ai≤bi a i ≤ b i 的怪打,然后再打 ai>bi a i > b i 的怪.
对于 ai≥bi a i ≥ b i 的怪,显然先打 ai a i 较小的可以最小化初始血量,而对于 ai>bi,aj>bj a i > b i , a j > b j 的两个怪,若先打 i i 再打 j j 那么血量的最小值为 HP+min(−ai,−ai+bi−aj) H P + m i n ( − a i , − a i + b i − a j ) ,而先打 j j 后打 i i 的血量最小值为 HP+min(−aj,−aj+bj−ai) H P + m i n ( − a j , − a j + b j − a i ) ,若 bi≤bj b i ≤ b j ,则由 bi≤bj<aj b i ≤ b j < a j 有 −ai+bi−aj<−ai − a i + b i − a j < − a i 以及 −ai+bi−aj≤−aj+bj−ai − a i + b i − a j ≤ − a j + b j − a i ,且由 ai>bi a i > b i 有 −ai+bi−aj≤−aj − a i + b i − a j ≤ − a j ,此时 HP+min(−ai,−ai+bi−aj)≤HP+min(−aj,−aj+bj−ai) H P + m i n ( − a i , − a i + b i − a j ) ≤ H P + m i n ( − a j , − a j + b j − a i ) ,故对于 ai>bi a i > b i 的应该先打 bi b i 大的怪,以此可以确定打这 n n 个怪的顺序 p1,...,pn p 1 , . . . , p n
之后考虑父亲的限制,假设处理点 pi p i 的时候, pi p i 的父亲已经被处理掉,那么 pi p i 直接处理就行,但若 pi p i 父亲之前没有被处理,那么将 pi p i 与其父亲合并为一个新的节点放入剩下节点中重新确定顺序即可,实现上述操作需要支持插入且保证有序性,可以用一个堆来维护,而将两点合并可以用并查集维护
具体操作为,预处理每点 x x 的父亲节点 pa[x] p a [ x ] ,每次在并查集中 find(pa[x]) f i n d ( p a [ x ] ) 即为找到合并后 x x 的父亲节点,由于在将 v v 和其父亲节点 u u 合并时,无法很快的在堆中删掉 u u ,可以用一个标记数组标记 u u ,之后在 u u 出现在堆顶时直接删去 u u 即可,为方便标记,在 u,v u , v 合并时我们可以把合并后的节点编号依旧记为 v v ,且实际删去 u u 点,把 v v 的所有兄弟变成 v v 的儿子,然后将 pa[v] p a [ v ] 变成 pa[u] p a [ u ] ,这样即可将 u u 的全部信息合并入 v v 中
注意, (a1,b1) ( a 1 , b 1 ) 和 (a2,b2) ( a 2 , b 2 ) 合并并不是简单的将 a,b a , b 加一起构成一个新的怪物,假如先处理 (a1,b1) ( a 1 , b 1 ) ,那么打第一个怪时需要的最小血量为 a1 a 1 ,打完后剩余血量为 b1 b 1 ,而打第二个怪时需要的血量为 max(a2−b1,0) m a x ( a 2 − b 1 , 0 ) ,故总需要血量为 a=a1+max(a2−b1,0) a = a 1 + m a x ( a 2 − b 1 , 0 ) ,而打完后剩余血量为 b=b2+max(b1−a2,0) b = b 2 + m a x ( b 1 − a 2 , 0 ) ,也即 (a1,b1)+(a2,b2)=(a,b) ( a 1 , b 1 ) + ( a 2 , b 2 ) = ( a , b )
Code
#include
#include
#include
#include
using namespace std;
typedef long long ll;
#define maxn 100005
struct node
{
ll a,b;
int id;
node(){};
node(ll _a,ll _b,int _id)
{
a=_a,b=_b,id=_id;
}
bool operator<(const node&x)const
{
if(a-b<=0&&x.a-x.b<=0)return a>x.a;
if(a-b>0&&x.a-x.b>0)return breturn a-b>x.a-x.b;
}
}m[maxn];
node merge(node x,node y)
{
return node(x.a+max(y.a-x.b,0ll),y.b+max(x.b-y.a,0ll),y.id);
}
priority_queueque;
int T,n,fa[maxn],pa[maxn],vis[maxn];
vector<int>g[maxn];
void dfs(int u,int f)
{
pa[u]=f;
for(int i=0;iint v=g[u][i];
if(v==f)continue;
dfs(v,u);
}
}
int find(int x)
{
if(x==fa[x])return x;
return fa[x]=find(fa[x]);
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=2;i<=n;i++)
{
scanf("%lld%lld",&m[i].a,&m[i].b);
m[i].id=i;
que.push(m[i]);
}
for(int i=1;i<=n;i++)g[i].clear(),vis[i]=0,fa[i]=i;
for(int i=1;iint u,v;
scanf("%d%d",&u,&v);
g[u].push_back(v),g[v].push_back(u);
}
dfs(1,1);
node ans=node(0,0,0);
vis[1]=1;
while(!que.empty())
{
node now=que.top();
que.pop();
if(vis[now.id])continue;
int v=now.id,u=find(pa[v]);
if(vis[u])
{
ans=merge(ans,now);
vis[v]=1;
}
else
{
fa[u]=v;
pa[v]=pa[u];
vis[u]=1;
m[v]=merge(m[u],now);
que.push(m[v]);
}
}
printf("%lld\n",ans.a);
}
return 0;
}