QOJ9904 最小生成树

news/2025/2/4 20:50:01/文章来源:https://www.cnblogs.com/binbin200811/p/18698464

QOJ9904 最小生成树

  • 北京大学 2024 年《数据结构与算法A(实验班)》期末考试

有趣的图论。

思路

\(a\) 排序,优先连接较小的 \(a\) 所表示边权的边。

并查集维护暴力连接是 \(O(n^2)\) 的,显然不可以接受。

我们观察一下性质。

发现对于 \(a_i\) 来说,对应了一个满足 \(l+r=n\) 的最大的区间 \([l,r]\),其中 \(l,l+1,l+2,\dots,(l+r)/2\),依次与 \(r,r-1,r-2,\cdots,(l+r)/2+1\) 连边,下文 \([l,r]\) 无特殊说明同上文。

由于只会连接 \(n-1\) 条边,我们从每次连接的边的边权考虑。

做法一:

定义 \(f_i\) 为点 \(i\) 并查集的根,初始时 \(f_i=i\)

\(f\) 翻转,设为 \(f'\),即 \(f'_i=f_{n-i+1}\)

那么对于 \(a_i\),找出 \(f[l,(l+r)/2]\)\(f'[n-r+1,n-((l+r)/2+1)+1]\) 不相等的位置,就可以连上一条边。

\(f\)\(f'\) 哈希,然后二分 \(f\)\(f'\) 相同的长度,哈希判断。

哈希使用树状数组维护,并查集合并时按秩合并,且更新 \(f\)\(f'\) 数组与其哈希值。

哈希查询修改一次 \(O(\log n)\),二分找一条边查询哈希 \(\log n\) 次,找边复杂度 \(O(n\log^2 n)\)

按秩合并遍历 \(n\log n\) 个点,修改总复杂度 \(O(n\log^2 n)\)

总复杂度 \(O(n\log^2 n)\)

#include<bits/stdc++.h>
using namespace std;#define ll long long
#define mod 998244353
#define base 20090327
#define pii pair<int,int>
#define fi first
#define se secondconst int maxn=4e5+5;int n;
int fa[maxn];pii a[maxn];ll pw[maxn];vector<int>vec[maxn];struct treearray
{ll ts[maxn];inline int lowbit(int x){return x&(-x);}inline void add(int x,ll v){v=(v+mod)%mod*pw[x]%mod;for(;x<=n;x+=lowbit(x)) (ts[x]+=v)%=mod;}inline ll query(int x){ll sum=0;for(;x;x-=lowbit(x)) (sum+=ts[x])%=mod;return sum;}inline ll qry(int l,int r){return (query(r)-query(l-1)+mod)%mod*pw[n-l]%mod;}
}L,R;inline void init()
{pw[0]=1;for(int i=1;i<=n;i++) pw[i]=pw[i-1]*base%mod;
}
inline void updata(int x,int v){L.add(x,v-fa[x]);R.add(n-x+1,v-fa[x]);fa[x]=v;}
inline void merge(int x,int y)
{x=fa[x],y=fa[y];if(vec[x].size()<vec[y].size()) swap(x,y);for(auto v:vec[y]) updata(v,x),vec[x].emplace_back(v);
}
inline pii check(int l,int r)
{if(L.qry(l,r)==R.qry(n-r+1,n-l+1)) return {0,0};int LL=1,rr=r-l+1;while(LL<rr){int mid=(LL+rr)>>1;if(L.qry(l,l+mid-1)==R.qry(n-r+1,n-r+mid)) LL=mid+1;else rr=mid;}return {l+LL-1,r-rr+1};
}int main()
{scanf("%d",&n);init();for(int i=3;i<n*2;i++) scanf("%d",&a[i].fi),a[i].se=i;sort(a+3,a+n*2);for(int i=1;i<=n;i++) updata(i,i),vec[i].emplace_back(i);ll ans=0;for(int i=3;i<n*2;i++){pii tmp={0,0};while((tmp=check(max(a[i].se-n,1),min(a[i].se-1,n))).fi)merge(tmp.fi,tmp.se),ans+=a[i].fi;}printf("%lld",ans);
}

做法二:

如果你做过 SCOI2016 萌萌哒 这样的并查集合并一段的形式非常相像,这题中我们同样使用倍增来维护并查集。

对于第 \(k\) 层,当 \(1\leq i\leq n\) 时,\(f[i][k]\) 表示包括 \(i\) 向后 \(2^k\) 个点与 \(f[i][k]\) 向前/后 \(2^k\) 个点相同;当 \(n+1\leq i\leq 2\times n\) 时,\(f[i][k]\) 表示包括 \(i\) 向前 \(2^k\) 个点与 \(f[i][k]\) 向前/后 \(2^k\)​ 个点相同。

注意这里的前后仅取决于 \(i\) 是否小于等于 \(n\)

\(k=0\) 时,若 \(i\leq n\)\(f[i][k]=i\);否则,\(f[i][k]=i-n\)

\(k>0\) 时,\(f[i][k]=i\)

每次查询时并查集需要返回,通过 \(a_i\)\([l,(l+r)/2]\)\([r,(l+r)/2+1]\) 之间新连接了多少条边。

查询 \((l,r,k)\),若 \(f[l][k]\)\(f[r][k]\) 不属于同一个并查集,我们向 \((l,r,k-1)\)\((l+2^{k-1},r-2^{k-1},k-1)\) 查询并将 \(f[l][k]\)\(f[r][k]\) 合并。

\(k=0\)\(l\)\(r\) 还是不在同一个块内,那么就需要连一条边。

每一层的有 \(n\) 个节点,最多两两合并 \(n-1\) 次,一共 \(\log n\) 层,复杂度 \(O(n\log n)\)

递归常数稍微大点。

#include<bits/stdc++.h>
using namespace std;#define ll long long
#define pii pair<int,int>
#define fi first
#define se secondconst int maxn=4e5+5;struct DSU
{int f[maxn];inline void init(int n){for(int i=1;i<=n*2;i++) f[i]=i;}inline int fr(int u){return u==f[u]?u:f[u]=fr(f[u]);}inline bool merge(int u,int v){u=fr(u),v=fr(v);if(u==v) return 0;f[u]=v;return 1;}
}F[20];int n;
int lg[maxn];pii a[maxn];inline void init(){for(int i=2;i<=n*2;i++) lg[i]=lg[i>>1]+1;}
inline int merge(int x,int y,int k)
{if(!F[k].merge(x,y+n)) return 0;if(k==0) return 1;return merge(x,y,k-1)+merge(x+(1<<(k-1)),y-(1<<(k-1)),k-1);
}int main()
{scanf("%d",&n);init();for(int i=3;i<n*2;i++) scanf("%d",&a[i].fi),a[i].se=i;sort(a+3,a+n*2);for(int i=0;i<=lg[n];i++){F[i].init(n);if(i==0) for(int j=n+1;j<=n*2;j++) F[i].f[j]=j-n;}ll ans=0;for(int i=3;i<n*2;i++){int l=max(a[i].se-n,1),r=min(a[i].se-1,n);int len=(r-l+1)/2,k=lg[len];int l1=l,r1=l+len-1,l2=r-len+1,r2=r;ans+=1ll*(merge(l1,r2,k)+merge(r1-(1<<k)+1,l2+(1<<k)-1,k))*a[i].fi;}printf("%lld",ans);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/878718.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一个Python GC的小Bug

这个代码是在看了“码农高天”的视频照模仿的。虽然 GC 能完成大部分内存管理的工作,但是基于性能计数器的GC确实存在小 Bug,这一点还是需要通过del和gc.collect手动管理。 from typing import Optionalclass Object:def __init__(self, name: str) -> None:self.obj: Opt…

2月做题记录

还有一个月。2月做题记录✩ trick ✯ 会大部分,要\(tj\)提示 ✬ 会小部分/完全没想到,看了\(tj\)才会 ◈ 脑电波 ✡ 有某一算法的神秘通用性质 ⊗ 待补目录2月做题记录字符串CF1827CCF1913FP10716P11150 ✩✯树[NOI2021] 轻重边做法1 ✩做法2做法3 ✡《简单树剖练习题》[QOJ9…

第六节 特征提取和无监督学习

没有标签y依旧提取出特征你的模型朝着哪个方向压缩特征,由你的标签来定自监督学习:从x中提取一部分作为自己的标签y,然后用x预测y 在自监督学习中,需要根据数据的特点设计一些自监督任务,这些任务通常基于数据的一些内在属性或关系来构建。例如,在图像数据中,可以设计图像…

产线MES 和 车间MES的区别与联系

制造执行系统(MES)在现代制造业中扮演着至关重要的角色,它连接着企业的计划层与生产现场,推动生产效率提升和质量管控强化。本文将深入探讨产线级MES和车间级MES的区别与联系,帮助读者更好地理解这两种MES系统如何协同工作,共同提升企业的生产管理水平。制造执行系统(ME…

CF995

A link如果选择这一天做题可以让差增加(即我这一天的题数大于他下一天的题数),则选;否则不选。 特殊的,另一个人地\(n+1\)天我们钦定他做了\(0\)道题。点击查看代码 #include<bits/stdc++.h>using namespace std;int n; int a[105],b[105];void qwq(){cin >> …

【qt】1个导航栏实现

//设定可增加按钮最大数量 void setBtnCountMax(int maxCount); const int btnCountMax() const;//设定背景颜色 void setBackGround(QColor bgcolor=QColor("black")); //设定按钮最小宽度 void setButtonMinWidth(int w); //设定内边距 void setPaddings(int left,…

09. Shell脚本

一、什么是ShellShell 是操作系统中的一个 命令行解释器,主要功能是接收用户命令,然后将这些命令传递给操作系统内核去执行。 Shell 是用户与操作系统内核之间的接口,它允许用户通过命令行或脚本来与操作系统进行交互。同时,Shell 也是一种脚本语言,允许用户编写一系列命令…

Maven安装、配置以及Idea使用本地Maven仓库

Maven的安装和配置 Maven是Java项目常用的依赖管理和构建工具,项目使用Maven的一个特点就是包含一个pom.xml文件。实际上,Idea在安装时会集成一个Maven,这里不使用这个集成的,而是记录一下如何在机器上单独安装并配置好。 首先,需要安装好jdk。然后,到搜索引擎上搜索Mave…

DeepSeek逆天,核心是 知识蒸馏(Knowledge Distillation, KD),一项 AI 领域的关键技术

本文原文链接 文章很长,且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 : 免费赠送 :《尼恩Java面试宝典》 持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备 免费赠送 :《尼恩技术圣经+高并发系列PDF》 ,帮你 …

Java初学者笔记-12、前端开发基础

未完待续 Web 三个组成部分:HTML:负责网页的结构(页面元素和内容)。 CSS:负责网页的表现(页面元素的外观、位置等页面样式,如:颜色、大小等)。 Javascript:负责网页的行为(交互效果)。高级技术:基于JS封装而来的高级框架——vue。 基于vue的桌面端组件库——Eleme…

Java笔记-13、Web后端基础 Maven

Maven是一款用于管理和构建Java项目的工具,是apache旗下的一个开源项目。基于项目对象模型(POM),通过一小段描述信息管理项目构建。依赖管理:方便快捷地管理项目依赖的资源(jar包)。 项目构建:标准化的跨平台自动化项目构建方式。Maven面板中Lifecycle中的compile用于编译…

直流有刷电机.

一、基础知识回顾 1.1 电路定律 1.1.1 基尔霍夫电流定律 在集总参数电路中的任一节点处,所有支路电流的代数和在任何时刻恒等于零,即\(\sum i=0\)。 对于正弦稳态交流电路,其相量形式为\(\sum {\dot I}=0\)。 其中,当支路电路的参考方向为流入、流出节点时,支路电流分别取…