学习笔记——根号分治

根号分治

根号分治与其说是一个算法,更不如说是一种思想

例题1

给出一个正整数x,和n个整数ai,求xaimodp

对于100%的数据,1n106,1x,ai<p,p=998244352

这很显然可以用二分快速幂来解决,时间复杂度O(nlogn),但如果时限为0.2s呢,二分快速幂就有一点乏力了。

因为发现底数x不变,考虑打表,将xk全部预处理出来,最后直接查询。预处理,时间复杂度O(k),空间复杂度O(k),查询,时间复杂度O(1),这也不行。

但将两种方法结合一下,根号分治,这道题就可以做了:

因为xk=xk%p+pk/p=xk%pxpk/p

那么p的取值就至关重要,根号分治其实就是p=n,然后分类讨论

对于k<=n时,我们可以O(n)预处理[1,n]xk

对于k>n

可以先O(n)求出(1<=i<=n)xik/p

然后通过xk=xk%p+pk/p=xk%pxpk/p求得最后答案

预处理O(n),查询O(1)

code:

#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只叫做“doge”的神秘生物在雅加达市居住。它们的编号一次是0M1。编号为idoge最初居住于标号为Bi的摩天楼。每只doge都有一种神秘的力量,使它们能够在摩天楼之间跳跃,编号为idoge的跳跃能力为Pi(Pi>0)。在一次跳跃中,位于摩天楼 b 而跳跃能力为 pdoge 可以跳跃到编号为 bp (如果 0bp<N)或 b+p (如果 0b+p<N)的摩天楼。。
编号为0doge是所有doge的首领,它有一条紧急的消息要尽快传送给编号为1doge。任何一个收到消息的doge有以下两个选择:
1.跳跃到其他摩天楼上;
2.将消息传递给它当前所在的摩天楼上的其他doge。
请帮助doge们计算将消息从0doge传递到1号的doge所学要的最少总跳跃步数,或者告诉他们消息永远不可能传递到1doge

1<=N<=30000,1<=Pi<=30000,2<=M<=30000

图论,边数太大,考虑根号分治优化建图,这道题对doge的跳跃能力进行分治

拆点,把每个点拆出n个点,分别代表距离1,2,...,n

对于点i

i只连接与i距离为1的点
i只连接与i距离为2的点
i...只连接与i距离为n的点

把两两距离为1的点连起来
把两两距离为2的点连起来
......
把两两距离为n的点连起来
i,i都往i连边,长度为0
表示到达ii都等价于到达i

x号点上的doge跳跃距离为p,且p>n,则直接连边到它能到达的点

这样建边最多nn

最后跑最短路即可

三元环

给出一张n个点,m条边的无向图,问图中有多少个三元环{a,b,c},满足图中存在{ab,bc,ca}

方法一:

转换,对边定向,边的方向由度数较小的节点指向度数较大的节点。若度数相同,则按照节点编号由大到小连边。

结论1,新图一定是有向无环图(DAG)

证明:
反证,假设存在一个环:a>b>c>a
di表示原图中i号点的度数,那么有da<=db<=dc<=da,该式子要成立,必须满足da=db=dc=da,度数相同的节点由小到大连边,那么有a>b>c>a,所以a>a,矛盾,证毕

结论2,新图每个点的出度不超过m

证明:

假设在无向图中点u的度<=m,定向后其出度肯定不会大于等于它在原图中的度,所以outu<=m

假如在无向图中u的度>m,假如有边u>v,那么原图中u的度小于等于v的度,又因为原图中u的度是>m的,所以满足条件的点v不超过m个,于是这种情况下outu<m,证毕

统计三元环

  1. 枚举每一个点,对于当前点,将它能到达的点打上标记
  2. 枚举做过标记的点,对于当前被做过标记的点,枚举它能到达的点,假如这个能到达的点是被做过标记的,那么就找到了一个三元环。

三元环是定向的,每个点只会枚举出边,所以每个环被统计的情况是唯一的。

**共n个点,每个点出度不超过m,所以时间复杂度为O(nm)

code:求三元环贡献和maxa1,a2,a3

#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;
}

方法二:

无向图上进行根号分治(常数较大)

  1. 统计每个点的度数,度数<=m的分为第1类,度数>m分为第2
  2. 对于第1类,暴力枚举每个点,然后暴力枚举这个点相连的任意两条边,再判断这两条边的另一个端点是否相连(因为m条边最多被遍历一次,出度<=m,所以复杂度为O(mm)
  3. 对于第2类,直接暴力枚举任意三个点,判断这三个点是否构成环(因为这一类点的个数不会超过m,所以复杂度为O((m)3)=O(mm)
  4. 判断两个点是否连接可以用set,map,hash

code


posted @   Thermalrays  阅读(717)  评论(1编辑  收藏  举报
编辑推荐:
· 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框架的用法!
点击右上角即可分享
微信分享提示