学习笔记——根号分治
根号分治
根号分治与其说是一个算法,更不如说是一种思想
例题1
给出一个正整数,和个整数,求
对于的数据,
这很显然可以用二分快速幂来解决,时间复杂度,但如果时限为呢,二分快速幂就有一点乏力了。
因为发现底数不变,考虑打表,将全部预处理出来,最后直接查询。预处理,时间复杂度,空间复杂度,查询,时间复杂度,这也不行。
但将两种方法结合一下,根号分治,这道题就可以做了:
因为
那么的取值就至关重要,根号分治其实就是,然后分类讨论
对于时,我们可以预处理的
对于
可以先求出的
然后通过求得最后答案
预处理,查询
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define ri register int
static char buf[1000000],*p1=buf,*p2=buf,obuf[1000000],*p3=obuf;
#define getchar() p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++
#define putchar(x) (p3-obuf<1000000)?(*p3++=x):(fwrite(obuf,p3-obuf,1,stdout),p3=obuf,*p3++=x)
template<typename item>
inline void read(register item &x)
{
x=0;register char c=getchar();
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
}
static char cc[10000];
template<typename item>
inline void print(register item x)
{
register long long len=0;
while(x)cc[len++]=x%10+'0',x/=10;
while(len--)putchar(cc[len]);
}
typedef long long ll;
const ll mod = 998244352;
int n,x,a;
const int MAX_N = 32000;
ll f[MAX_N],ff[MAX_N];
signed main(){
// freopen("P.in","r",stdin);
// freopen("P.out","w",stdout);
read(x),read(n);
f[0]=ff[0]=1;
int m=sqrt(mod);
for(int i=1;i<=m;i++) f[i]=f[i-1]*x%mod;
for(int i=1;i<=m;i++) ff[i]=ff[i-1]*f[m]%mod;
for(ri i=1;i<=n;i++){
read(a);
print(f[a%m]*ff[a/m]%mod);
putchar(' ');
}
fwrite(obuf,p3-obuf,1,stdout);
return 0;
}
例题4:雅加达的摩天楼
印尼首都雅加达市有N座摩天楼,它们排列成一条直线,我从左到右依次将它们编号为0到N-1。除了这N座摩天楼外,雅加达市没有其他摩天楼。
有M只叫做“”的神秘生物在雅加达市居住。它们的编号一次是到。编号为的最初居住于标号为的摩天楼。每只都有一种神秘的力量,使它们能够在摩天楼之间跳跃,编号为的的跳跃能力为。在一次跳跃中,位于摩天楼 b 而跳跃能力为 的 可以跳跃到编号为 (如果 )或 (如果 )的摩天楼。。
编号为的是所有doge的首领,它有一条紧急的消息要尽快传送给编号为的。任何一个收到消息的有以下两个选择:
1.跳跃到其他摩天楼上;
2.将消息传递给它当前所在的摩天楼上的其他doge。
请帮助们计算将消息从号传递到号的所学要的最少总跳跃步数,或者告诉他们消息永远不可能传递到号。
图论,边数太大,考虑根号分治优化建图,这道题对的跳跃能力进行分治
拆点,把每个点拆出个点,分别代表距离
对于点,
只连接与距离为的点
只连接与距离为的点
只连接与距离为的点
把两两距离为的点连起来
把两两距离为的点连起来
把两两距离为的点连起来
都往连边,长度为
表示到达或都等价于到达
若号点上的跳跃距离为,且,则直接连边到它能到达的点
这样建边最多边
最后跑最短路即可
三元环
给出一张个点,条边的无向图,问图中有多少个三元环,满足图中存在
方法一:
转换,对边定向,边的方向由度数较小的节点指向度数较大的节点。若度数相同,则按照节点编号由大到小连边。
结论1,新图一定是有向无环图()
证明:
反证,假设存在一个环:
设表示原图中号点的度数,那么有,该式子要成立,必须满足,度数相同的节点由小到大连边,那么有,所以,矛盾,证毕
结论2,新图每个点的出度不超过
证明:
假设在无向图中点的度,定向后其出度肯定不会大于等于它在原图中的度,所以
假如在无向图中的度,假如有边,那么原图中的度小于等于的度,又因为原图中的度是的,所以满足条件的点不超过个,于是这种情况下,证毕
统计三元环
- 枚举每一个点,对于当前点,将它能到达的点打上标记
- 枚举做过标记的点,对于当前被做过标记的点,枚举它能到达的点,假如这个能到达的点是被做过标记的,那么就找到了一个三元环。
三元环是定向的,每个点只会枚举出边,所以每个环被统计的情况是唯一的。
**共个点,每个点出度不超过,所以时间复杂度为
求三元环贡献和
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX_N = 100000 + 5;
const int MAX_M = 250000 + 5;
struct node{
int a,b;
}edge[MAX_M];
int n,m,d[MAX_N];
ll a[MAX_N];
vector<int> v[MAX_N];
int mark[MAX_N];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
for(int i=1;i<=m;i++){
scanf("%d%d",&edge[i].a,&edge[i].b);
d[edge[i].a]++,d[edge[i].b]++;
}
for(int i=1;i<=m;i++){
int x=edge[i].a,y=edge[i].b;
if(d[x]>d[y] || (d[x]==d[y] && x<y)) swap(x,y);
v[x].push_back(y);
}
ll ans=0;
for(int x=1;x<=n;++x){
for(int i=0;i<v[x].size();i++){
int y=v[x][i];
mark[y]=x;
}
for(int i=0;i<v[x].size();i++){
int y=v[x][i];
for(int j=0;j<v[y].size();j++){
if(mark[v[y][j]]==x){
ans+=max(a[v[y][j]],max(a[x],a[y]));
}
}
}
}
printf("%lld",ans);
return 0;
}
方法二:
无向图上进行根号分治(常数较大)
- 统计每个点的度数,度数的分为第类,度数分为第类
- 对于第类,暴力枚举每个点,然后暴力枚举这个点相连的任意两条边,再判断这两条边的另一个端点是否相连(因为条边最多被遍历一次,出度,所以复杂度为)
- 对于第类,直接暴力枚举任意三个点,判断这三个点是否构成环(因为这一类点的个数不会超过,所以复杂度为
- 判断两个点是否连接可以用等
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!