一种好像之前做过这道题的感觉,然后发现……没用。
题目大意
你现在正在打一个游戏”Monster Hunter“,游戏中你在一个有 个节点的地图上,一共有 条双向边相连,初始时你在节点1上,初始HP值为 ,节点2,3,…,n各有一个怪兽,要消灭第 个怪兽需要 点HP,在消灭第 个怪兽之后又可以得到 的HP,消灭第 个怪兽需要先把他和你之间所有怪兽全部消灭。求消灭所有怪兽所需要的最小初始HP值 。
解题报告
看了官方Blog 10min后自己动手敲,然后发现难以实现……
官方Blog的题解还是很好懂的,以1为根建有根树,如果消灭第 个怪兽就需要把他父节点怪兽消灭掉,所以有父节点限制,所以先考虑把这个限制取消,即给出 个怪兽,求消灭他们所需的最小HP值。
不妨把所有怪兽分成两类: 和 ,前者打完加血,后者扣血,然后发现先打前者明显更划算。
分类讨论:
: 明显把a从小到大打更优。
:这里可以使用贪心常用的推论方法,设两个变量 , ,若 比 更优,则
再分三类:
1. 且 ,则如果 ,能推出 ,与前提不符,反之亦然,故舍去。
2. 且 或
假设 ,则
此时
∴ 即 ,与前提不符,故舍去。
3. 且 ,则
,
即
(原来Blog就这几句不明所云的话,所以这里我写了比较长的证明,比较蠢,看看就好)
综上,若 ,按照 从大到小消灭。
所以以上,我们已经得出了在没有父节点限制下的处理顺序 。
然后带回原题,如果有父节点限制,该怎么办?
又分类:
1.如果 ,即 没有父亲限制,那么第一步打 一定最优。
2.如果 ,即 有父亲限制,那么在处理完 的父亲后紧接着处理 一定最优,所以可以将 和他的父亲合并,计算出新的 (如何计算见代码+=),并将 所有儿子节点的父亲设为 的父亲。
这样的话 的问题就解决了,将规模为 的问题化成了规模为 的问题,然后重复操作直到只剩下一只怪兽即可求出最少所需血量。
然后求最优策略带插入,堆
求父亲待修改,并查集
然后……好了?
示例代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100005
#define maxe 200005
using namespace std;
typedef long long LL;
int n,len,tot,tst,lnk[maxn],son[maxe],nxt[maxe],fa[maxn],father[maxn],num[maxn];
struct data{
LL x,y; int id,s;
data (LL x=0,LL y=0,int id=0,int s=0):x(x),y(y),id(id),s(s){}
bool operator < (const data b)const{
if (x<y&&b.x>=b.y) return 1;
if (x>=y&&b.x<b.y) return 0;
if (x<y&&b.x<b.y) return x<b.x||(x==b.x&&y>b.y);
if (x>=y&&b.x>=b.y) return y>b.y||(y==b.y&&x<b.x);
}
data operator += (const data t){
LL a=max(x,x-y+t.x),b=y-x+t.y-t.x+a;
x=a; y=b; return *this;
} //将该节点的怪兽与父节点合并
}a[maxn];
struct hep{
data hep[maxn];
void put(data a){
hep[++len]=a;
for (int son=len;son!=1&&hep[son]<hep[son>>1];son>>=1) swap(hep[son],hep[son>>1]);
}
data get(){
data ans=hep[1]; hep[1]=hep[len--];
for (int fa=1,son;(fa<<1)<=len;fa=son){
son=fa<<1;
if (son<len&&hep[son+1]<hep[son]) son++;
if (hep[son]<hep[fa]) swap(hep[son],hep[fa]); else break;
}
return ans;
}
}h; //手写堆……
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void readi(int &x){
x=0; char ch=nc();
while ('0'>ch||ch>'9') ch=nc();
while ('0'<=ch&&ch<='9') {x=x*10+ch-'0'; ch=nc();}
}
void _add(int x,int y){
son[++tot]=y; nxt[tot]=lnk[x]; lnk[x]=tot;
}
void _dfs(int x,int dad){
for (int j=lnk[x];j;j=nxt[j])
if (son[j]!=dad){father[son[j]]=x; _dfs(son[j],x);}
}
void _init(){
readi(n); tot=len=0; a[1]=data(0,0,1,0);
for (int i=1,j=1;i<=n;i++,j+=2) lnk[i]=son[j]=son[j+1]=nxt[j]=nxt[j+1]=0;
for (int i=2,x,y;i<=n;i++) {readi(x); readi(y); a[i]=data(x,y,i,0); h.put(a[i]);}
for (int i=1,x,y;i<n;i++) {readi(x); readi(y); _add(x,y); _add(y,x);}
}
int f_get(int x){return fa[x]==x?fa[x]:fa[x]=f_get(fa[x]);}
void _solve(){
_dfs(1,0); int p=0;
for (int i=1;i<=n;i++) {fa[i]=i; num[i]=0;}
while (len){
data b=h.get(); int x=b.id;
if (b.s!=num[x]) continue; //num[x]是更新次数,以防在堆中选重
int dad=f_get(father[x]); a[dad]+=a[x]; fa[x]=dad;
if (dad>1){
a[dad].s=num[dad]=++p; h.put(a[dad]);
}
}
printf("%lld\n",a[1].x);
}
int main()
{
freopen("hunter.in","r",stdin);
freopen("hunter.out","w",stdout);
readi(tst);
while (tst--){
_init();
_solve();
}
return 0;
}
一个优先级刚了我1d的题目……