HHHOJ #1252. 「NOIP 2023 模拟赛 20230716 B」打怪兽 思考--zhengjun
赛时卡了很久,没想出来怎么做。
随便乱搞了个优先级队列,过拍直接走人,然后成为了唯一的 100 分,无语了。
完事后发现这样做确实是对的,而且好写,简单讲一下。
首先需要能够判断打两个怪 的优先级:
-
,则优先攻击 较小的那个;
-
,则优先攻击 较大的那个;
-
否则,优先攻击 的那个。
由于有拓扑序的限制,所以考虑从下往上解决问题。
假设已经知道了 的儿子 的攻击序列,且儿子的攻击序列均已有序。
那么显然, 只需要把所有的攻击序列归并一下就完事了。
但是,此时需要在序列的最开头加入 ,可能会破坏原来的顺序。
记 表示的怪为 ,儿子归并后序列的第一项表示的怪为 。
若 ,说明攻击完 时,可以继续攻击比 还要优的怪。
这里有个隐含条件:攻击 代表着 已经是现有中最优的怪,而 比 优,所以攻击完 后一定会攻击 。
那么我们不妨直接合并这两个怪,并把 移出原序列。
继续重复上述步骤直到 可以满足序列的单调性为止。
时间复杂度:,取决于写的是可并堆还是启发式合并。
代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=1e5+10;
int n,m;
struct zj{
ll a,b;
zj operator + (const zj &x)const{
if(x.a>b)return {a+x.a-b,x.b};
return {a,b+x.b-x.a};
}
bool operator < (const zj &x)const{
int t1=a<=b,t2=x.a<=x.b;
if(t1^t2)return t1;
if(t1)return a<x.a;
return b>x.b;
}
}a[N];
struct tree{
zj x;
int ls,rs,dis;
}t[N];
void merge(int &x,int y){
if(!x||!y){
x|=y;return;
}
if(t[y].x<t[x].x)swap(x,y);
merge(t[x].rs,y);
if(t[t[x].ls].dis<t[t[x].rs].dis)swap(t[x].ls,t[x].rs);
t[x].dis=t[t[x].rs].dis+1;
}
void pop(int &x){
int r1=t[x].ls,r2=t[x].rs;
merge(x=r1,r2);
}
int root[N];
vector<int>to[N];
void dfs(int u,int fa=0){
for(int v:to[u])if(v^fa){
dfs(v,u);
merge(root[u],root[v]);
}
for(;root[u]&&t[root[u]].x<a[u];pop(root[u])){
a[u]=a[u]+t[root[u]].x;
}
t[u]={a[u],0,0,1},merge(root[u],u);
}
int main(){
freopen("monster.in","r",stdin);
// freopen("monster.out","w",stdout);
scanf("%d",&n);
for(int i=2;i<=n;i++)scanf("%lld%lld",&a[i].a,&a[i].b);
for(int i=1,u,v;i<n;i++){
scanf("%d%d",&u,&v);
to[u].push_back(v),to[v].push_back(u);
}
dfs(1);
zj ans={0,0};
for(;root[1];pop(root[1]))ans=ans+t[root[1]].x;
cout<<ans.a;
return 0;
}
分类:
HHHOJ
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】