hdu6326 有一些怪物构成了树形结构,在打第i只怪的时候,会先扣ai点血量,再扣bi点血量。要求在打一只怪之前必须先打它父节点位置上的怪,问要使这个过程中的血量不会降低到0以下,初始的时候最少要有多少点血。
考虑打怪的先后顺序,对于i,j两只怪物,如果先打i,这个过程中的最低血量就是A=min(-ai,-ai+bi-aj),反之如果先打j就是B=min(-aj,-aj+bj-ai)。显然如果A>B就需要先打i,否则就先打j。
但是直接贪心是不正确的,因为有必须先打父节点再打子节点的条件。于是我们就在一开始把所有的点放进优先队列中,每次将选出的节点和它的父节点合并成一个节点,合并的时候同样也遵循贪心的原则。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
#define maxn 100050
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;
int t,n,m,pre[maxn];
bool vis[maxn];
vector<int>maze[maxn];
ll Min(ll a,ll b){return a<b?a:b;}
struct node
{
ll x,y;
int id,num;
bool operator < (const node& o)const
{
return Min(x,x+o.x+y) < Min(o.x,o.x+x+o.y);
}
}e[maxn];
priority_queue<node>q;
int Find(int x)
{
if(!vis[pre[x]])return pre[x];
return pre[x]=Find(pre[x]);
}
void init()
{
for(int i=1;i<=n;i++)
maze[i].clear(),pre[i]=i;
memset(vis,0,sizeof(vis));
//memset(head,-1,sizeof(head));
}
void dfs_pre(int u,int fa)
{
pre[u]=fa;
int len=maze[u].size();
for(int i=0;i<len;i++)
{
int v=maze[u][i];
if(v!=fa)dfs_pre(v,u);
}
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
init();
e[1].x=e[1].y=0,e[1].id=1,e[1].num=0;
for(int i=2;i<=n;i++)
{
scanf("%lld%lld",&e[i].x,&e[i].y);
e[i].x*=-1;
e[i].id=i;
e[i].num=0;
}
int u,v;
for(int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
maze[u].push_back(v);
maze[v].push_back(u);
}
dfs_pre(1,1);
while(!q.empty())q.pop();
for(int i=2;i<=n;i++)
q.push(e[i]);
int cnt=0;
while(!q.empty())
{
node now=q.top();
q.pop();
int u=now.id;
if(vis[u])continue;
if(now.num!=e[u].num)continue;
vis[u]=1;
int fa=Find(u);
if(e[u].x+e[fa].y<0)
{
e[fa].x+=(e[u].x+e[fa].y);
e[fa].y=e[u].y;
}
else e[fa].y+=(e[u].x+e[u].y);
if(fa>1)
{
e[fa].num=++cnt;
q.push(e[fa]);
}
}
printf("%lld\n",-e[1].x);
}
return 0;
}