Ybt 金牌导航 6.2.F 大根堆 / BZOJ 4919 大根堆(LIS,启发式合并)
题意简述
有一个以
解法
原题限制相当于:在选出的点集构成的新树
不会最长上升子序列的建议前往这里。
做法:对每个节点维护一个 set,合并子树的 set 时用启发式合并,插入元素时寻找第一个大于等于它本身的元素,删除原来的元素,并加入这个元素。若没有,直接加入。最终根节点的 set 大小就是答案。
这个做法为什么是对的?
这里 set 里维护的数相当于某个 LIS 上,长度为某值时该节点作为 LIS 末尾的元素值最小。换句话说,set 内的每个元素维护的是一条 LIS,该元素为 LIS 的末尾。
首先,求 LIS 的一个经典贪心结论:长度一定时,将末尾元素替换为更小的元素肯定不劣。(这样更有利于我们之后扩展该 LIS 的长度)
插入元素时:
- 若它是最大的元素,直接插入显然没有问题。
- 若它不是最大的元素,找到第一个大于等于它的数,该数对应的 LIS 的倒数第二个元素一定没有要插入的元素大,根据求 LIS 的经典贪心结论,将该 LIS 的末尾元素替换成药插入的元素显然不劣。
时间复杂度
代码略去缺省源。
点击查看代码
int n,m,a[maxn];
vector<int>G[maxn];
multiset<int>s[maxn];
void merge(multiset<int>&x,multiset<int>&y){
if(x.size()<y.size())swap(x,y);
while(!y.empty())x.insert(*y.begin()),y.erase(y.begin());
}
void dfs(int x){
for(int u:G[x]){
dfs(u);merge(s[x],s[u]);
}
auto it=s[x].lower_bound(a[x]);
if(it!=s[x].end())s[x].erase(it);
s[x].insert(a[x]);
}
void solve_the_problem(){
n=rd(),a[0]=inf;
rep(i,1,n){
int x;a[i]=rd(),x=rd();
G[x].pb(i);
}
dfs(1);
write(s[1].size());
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现