当前位置: 首页 > news >正文

记一类树上点覆盖问题

1

有一棵树上有 \(n\) 个点,每个点都要被覆盖。

现在可以选一些点放置覆盖器,每个覆盖器可以与它 \(\le 0\) 的点,求最少放置数及方案。

这个显然是给每个点放一个覆盖器。

2

#1 中的每个覆盖器覆盖的距离换成 \(1\)

DP 输出方案很困难,考虑贪心。

随便选一个点作为根,记 \(f_x\) 表示点 \(x\) 的父亲。

不难想到按深度从大到小考虑,对于一个已经被覆盖的点直接略过。

现在如果点 \(x\) 没被覆盖,显然选 \(f_x\) 放覆盖器更优。因为 \(x\) 往下的节点已经全被覆盖,放 \(f_x\) 比放 \(x\) 还能多覆盖一些更上面的点。

对于每个 \(x\),判断是否被覆盖只要看 \(x,f_x\) 中有没有覆盖器即可,时间复杂度 \(\mathcal O(n)\)

3

#1 中的每个覆盖器覆盖的距离换成 \(k\)

可以像 #2 一样贪心放 \(k\) 级祖先,但这样判断是否被覆盖是 \(\mathcal O(k)\) 的,总时间复杂度为 \(\mathcal O(nk)\)

但可以发现一个性质,就是当 \(k\) 大的时候要放覆盖器的数量是不多的。感性理解一下就是当覆盖的范围很大,那么覆盖器就能覆盖很多的点,这样覆盖器的数量就不会多。

严谨一点的话可以考虑最坏情况,即一条链。此时要放覆盖器的数量是 \(\mathcal O(\dfrac{n}{k})\) 的。

所以考虑平衡修改(放覆盖器)与查询(判断是否被覆盖)的复杂度。

对于放覆盖器的点 \(x\),把要标记的点分成在它子树内和其它的两种。

对于第一种,将子树用 dfs 序变为区间。

发现如果只统计上面的覆盖器覆盖下面时,如果能覆盖到更下面的点时一定能覆盖到它。

那么我们设一个 dfs 序列它的意义为:

对于序列中的位置 \(i\),只统计上面的覆盖器覆盖下面时 \(i\) 对应的点 \(x\)\(\operatorname{dfn}(x)=i\)\(\operatorname{dfn}(x)\) 表示 \(x\)dfs 序中的编号)最大能被覆盖到多深,如果这个深度是 \(\ge x\) 的深度时 \(x\) 就是被覆盖的。

对于第二种,可以看作在 \(f_x\) 能覆盖子树中距离 \(\le k-1\) 的点,在 \(f_{f_x}\) 能覆盖子树中距离 \(\le k-2\) 的点,\(\dots\),是类似第一种的情况。

发现对于点 \(x\) 上面的覆盖器,我们用第一种统计了。对于 \(x\) 下面的覆盖器会被第二种统计,所以是正确的。

那么我们只需要做区间取 \(\max\),单点查询,可以用线段树维护。

这样的话处理第一种是 \(\mathcal O(\log n)\) 的,算上第二种一共 \(\mathcal O(k\log n)\),由于只会被修改 \(\mathcal O(\dfrac{n}{k})\) 次,所以总时间复杂度降为 \(\mathcal O(n\log n)\)

注意要判断是否存在 \(k\) 级祖先。

4

#3 但覆盖点不为全部点/覆盖距离会改变。

只要满足放 \(k\) 级祖先一定不劣就可以像 #3 一样做。

例题

P2279

\(k=2\) 时的 #3,当然 \(\mathcal O(nk)\) 也能过就是了。

代码略。

P3523

二分答案后就变为 #3 了。

#include<bits/stdc++.h>
#define N 300005
using namespace std;
vector<int>s[N];
int n,m,tot,a[N],p[N];
int fa[N],dep[N],siz[N],dfn[N];
class Segmentree{#define l(i) ((i)<<1)#define r(i) ((i)<<1|1)#define v(i) tr[i].valprivate:struct xs{int val;}tr[N<<2];public:void clear(){memset(tr,0,sizeof tr);}void upd(int ql,int qr,int v,int x=1,int l=1,int r=n){if(ql<=l&&qr>=r) return v(x)=max(v(x),v),void();int mid=l+r>>1;if(ql<=mid) upd(ql,qr,v,l(x),l,mid);if(qr>mid) upd(ql,qr,v,r(x),mid+1,r);}int query(int p,int x=1,int l=1,int r=n){if(l==r) return v(x);int mid=l+r>>1;v(l(x))=max(v(l(x)),v(x));v(r(x))=max(v(r(x)),v(x));if(p<=mid) return query(p,l(x),l,mid);return query(p,r(x),mid+1,r);}#undef l#undef r#undef v
}tr;
int dfs(int x){dep[x]=dep[fa[x]]+1;dfn[x]=++tot,siz[x]=1;for(auto p:s[x]) if(p^fa[x])fa[p]=x,siz[x]+=dfs(p);return siz[x];
}
bool check(int d){int cnt=0;tr.clear();for(auto x:p)if(a[x]&&tr.query(dfn[x])<dep[x]){if((++cnt)>m) return false;int t=x;for(int i=d;fa[t]&&i--;) t=fa[t];for(int i=d;t&&i>=0;t=fa[t],i--)tr.upd(dfn[t],dfn[t]+siz[t]-1,dep[t]+i);}return true;
}
int main(){scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)scanf("%d",a+i),p[i-1]=i;for(int i=1,u,v;i<n;i++)scanf("%d%d",&u,&v),s[u].emplace_back(v),s[v].emplace_back(u);dfs(1);int l=0,r=n-1;sort(p,p+n,[](int x,int y){return dep[x]>dep[y];});while(l<=r){int mid=l+r>>1;if(check(mid)) r=mid-1;else l=mid+1;}printf("%d",r+1);return 0;
}

P8428

先处理出每个点距离最近的羊的距离。

按深度贪心,发现一个点如果往父亲跳还能覆盖到它,那么一定不劣,分析是类似的。

不过貌似这题要覆盖的点有良好的性质,直接暴力也是对的。

#include<bits/stdc++.h>
#define N 500005
using namespace std;
bool a[N],b[N];
vector<int>s[N],ans;
int n,m,k,f[N],p[N];
int fa[N],dep[N],siz[N],dfn[N];
class Segmentree{#define l(i) ((i)<<1)#define r(i) ((i)<<1|1)#define v(i) tr[i].valprivate:struct xs{int val;}tr[N<<2];public:void upd(int ql,int qr,int v,int x=1,int l=1,int r=n){if(ql<=l&&qr>=r) return v(x)=max(v(x),v),void();int mid=l+r>>1;if(ql<=mid) upd(ql,qr,v,l(x),l,mid);if(qr>mid) upd(ql,qr,v,r(x),mid+1,r);}int query(int p,int x=1,int l=1,int r=n){if(l==r) return v(x);int mid=l+r>>1;v(l(x))=max(v(l(x)),v(x));v(r(x))=max(v(r(x)),v(x));if(p<=mid) return query(p,l(x),l,mid);return query(p,r(x),mid+1,r);}#undef l#undef r#undef v
}tr;
int dfs1(int x){dfn[x]=++m;f[x]=a[x]?-1:n;for(auto p:s[x]) if(p^fa[x])fa[p]=x,f[x]=min(f[x],dfs1(p));return ++f[x];
}
int dfs2(int x){siz[x]=1;dep[x]=dep[fa[x]]+1;f[x]=min(f[x],f[fa[x]]+1);for(auto p:s[x])if(p^fa[x]) siz[x]+=dfs2(p);return siz[x];
}
int main(){scanf("%d%d",&n,&k);for(int i=1,u,v;i<n;i++)scanf("%d%d",&u,&v),s[u].emplace_back(v),s[v].emplace_back(u);for(int i=1,x;i<=k;i++)scanf("%d",&x),a[x]=true;for(int i=1;i<=n;i++) p[i]=i;f[0]=n,dfs1(1),dfs2(1);sort(p+1,p+n+1,[](int x,int y){return dep[x]>dep[y];});for(int i=1;i<=n;i++)if(a[p[i]]&&tr.query(dfn[p[i]])<dep[p[i]]){int t=0;for(int x=p[i],j=0;f[x]==j;x=fa[x],j++) t=x;ans.emplace_back(t);for(int x=t,j=f[t];x&&j>=0;x=fa[x],j--)tr.upd(dfn[x],dfn[x]+siz[x]-1,dep[x]+j);}printf("%llu\n",ans.size());for(auto p:ans) printf("%d ",p);return 0;
}

等看到新的题再加上去。

http://www.jsqmd.com/news/283781/

相关文章:

  • 无人机三维路径规划,基于杜鹃鲶鱼优化算法CCO实现,考虑最低成本:路径、高度、威胁、转角的多无人机协同集群避障路径规划附代码
  • 基于SpringAI的在线考试系统-知识点管理与试题管理模块联合回归测试文档
  • Flink HBase SQL Connector RowKey 设计、Upsert 语义、维表 Join、缓存与写入调优
  • ubuntu系统中如何安装apt-fast
  • 1.22
  • 日前日内多阶段多时间尺度源荷储协调调度Matlab代码
  • 如何解决CKEditor粘贴Word文档时格式错乱问题?
  • 讲讲航佳传媒的解决问题能力强吗的真相
  • 2026免费高质量立体声环境录音素材网站推荐TOP10
  • 国产信创环境下CKEditor导入Excel数据会丢失样式吗?
  • 汽车制造行业网页开发,JAVA如何实现大文件的分块与续传?
  • 恐怖电影缺音效?2026免费惊悚音效库TOP10推荐
  • 2026年无锡口碑好的铜铸件厂家推荐,扬州市雪龙铜制品值得选吗?
  • 使用 LangChain Pyodide Sandbox 在安全环境中执行 Python 代码
  • 聊聊河南高性价比的舞蹈艺考培训公司,CDC舞蹈艺考值得选吗?
  • 2026年目前重切削的刀塔机定制选哪家,排刀机/4+4车铣/双主轴双排刀/46排刀机/36排刀机,刀塔机工厂需要多少钱
  • WordPress如何实现微信公众号图文中的公式一键转存?
  • 2026热门厂家盘点:磁力搅拌器行业分析及十大厂家推荐
  • 深聊深圳秀优国际会展市场口碑,看其值得推荐不?
  • 2026年上海高端网站建设公司哪家好?12个深耕网站建设行业的网站建设公司推荐
  • 2026年专业的换热器用无缝钢管厂家Top10
  • 网页富文本编辑器如何保留Word文档原始排版?
  • 计算机毕设 deadline 前 1 个月慌了?我用 “模块拆分法” 救回我的工程
  • Path Traversal Vulnerability in zlib untgz ≤ 1.3.1
  • 基于CodeSys和Raspberry Pi制作简单PLC
  • 8.6 统一标准:OpenTelemetry 核心概念与全链路追踪实现
  • 【2026最新】大模型学习指南:零基础入门,从概念到应用,程序员必备,建议收藏!
  • 2022年深圳中学自招真题(答案版)
  • 时序数据库 Apache IoTDB V2.0.6/V1.3.6 发布|新增查询写回功能,优化查询与同步性能
  • 2026年低楼层微通风系统窗定制源头厂家排名,阜积铝业表现亮眼