首页 > 其他分享 >根号分治入门

根号分治入门

时间:2024-02-06 22:00:26浏览次数:25  
标签:入门 int rep 分治 cin long 根号 define

在vp cf的时候遇到的算法,当时看着就是题目很清楚,要不就是我不会的数据结果,要不就是算法,想了一会想不出直接去看题解了。现在补一下。

根号分治虽然名字里面有“分治”但实际上和分治的关系并不大,根号分治更多的还是一种思想。
根号分治的思想是将询问根据一个阈值设为\(S\)分为两部分。两个部分用不同的算法处理,一部分可以用暴力直接处理,另一部分可以通过预处理和动态维护
根号算法是一种优雅的暴力!

涉及到跳多少步的题目一般都是根号分治。

例题

P3396 哈希冲突


思路:题目等价于让我们求从下标y开始,步长为x的所有数的和
考虑最暴力的暴力对于所有的询问,我们都从y开始跑一遍那么最坏可能会被卡到\(O(nm)\)
然后会发现当m比较大的时候,需要求和的数会很少,我们以\(\sqrt{n}\)为分界线
当步长也就是模数大于\(\sqrt{n}\)最多需要\(O(\sqrt{n})\)的复杂度就可求出
当步长也就是模数小于\(\sqrt{n}\)我们需处理出来需要查询的信息\(O(1)\)去查询
考虑怎么样预处理
\(s[i][j]\):表示步长为i,起点为j,所有数的和。
枚举起点计算对于每种步长的贡献需要的时间复杂度是\(O(n\sqrt{n})\)
考虑完查询考虑更新
更新的话每次我们需要维护s数组
计算更新当前值对于每种步长的贡献的影响。


#include <bits/stdc++.h> 
#define int long long
#define rep(i,a,b) for(int i = (a); i <= (b); ++i)
#define fep(i,a,b) for(int i = (a); i >= (b); --i)
#define pii pair<int, int>
#define pll pair<long long, long long>
#define ll long long
#define db double
#define endl '\n'
#define x first
#define y second
#define pb push_back

using namespace std;

int s[510][510];
void solve()
{
	int n,q;cin>>n>>q;
	vector<int>a(n+1);
	rep(i,1,n){
		cin>>a[i];
	}
	
	//预处理出来模数比较小的情况
	int m=sqrt(n);
	rep(i,1,n){
		rep(j,1,m){
			s[j][i%j]+=a[i];
		}
	}
	
	while(q--){
		char op;cin>>op;
		int x,y;cin>>x>>y;
		if(op=='A'){
			//模数较小直接查询预处理的结果
			if(x<=m){
				cout<<s[x][y]<<endl;
			}else{
				//较大的话暴力去做
				int ans=0;
				for(int i=y;i<=n;i+=x)	ans+=a[i];	
				cout<<ans<<endl;
			}
		}else{
			//改变一个数就需要动态的维护s数组
			rep(i,1,m)	s[i][x%i]+=y-a[x];
			a[x]=y;				
		}
	}		
}
signed main(){
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
//   	freopen("1.in", "r", stdin);
  	int _;
//	cin>>_;
//	while(_--)
	solve();
	return 0;
}

F. Remainder Problem


思路同上面的题目哈希冲突
不过这里m取严格的\(\sqrt(500000)\)会\(t\)


#include <bits/stdc++.h> 
#define int long long
#define rep(i,a,b) for(int i = (a); i <= (b); ++i)
#define fep(i,a,b) for(int i = (a); i >= (b); --i)
#define pii pair<int, int>
#define pll pair<long long, long long>
#define ll long long
#define db double
#define endl '\n'
#define x first
#define y second
#define pb push_back

using namespace std;

int s[800][800];
int a[500010];
void solve()
{
	
	//预处理出来模数比较小的情况
	int n=500000,m=sqrt(400000);
	
	int q;cin>>q;
	while(q--){
		int x,y,op;cin>>op>>x>>y;
		if(op==1){
			rep(i,1,m)	s[i][x%i]+=y;
			a[x]+=y;
		}else{
			if(x<=m){
				cout<<s[x][y]<<endl;
			}else{
				int ans=0;
				for(int i=y;i<=n;i+=x){
					ans+=a[i];
				}
				cout<<ans<<endl;
			}
		}
	}	
}
signed main(){
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
//   	freopen("1.in", "r", stdin);
  	int _;
//	cin>>_;
//	while(_--)
	solve();
	return 0;
}

E. Array Queries


看到大佬的一句话:要大胆的想,不要因为算法显然太慢,显然有错就放弃对这种算法的思考(因为这通常不会花费太多的时间)

很好的一道题目,dp和根号分治结合的题目

\(f[i][j]\):表示第i个数要加多少次i和j才能>n
考虑转移:

\[f[i][j]= \begin{cases} 1 \quad ,i+j+a[i]>n\\ f[i+a[i]+j]+1 \quad ,i+j+a[i]<=n\\ \end{cases} \]

如果\(j\)和\(a[i]\)暴力转移的话会是\(O(n^2)\)的复杂度
考虑根号分治当\(k\)比较大的时候会跳的比较快很快就会大于\(n\)这时我们直接暴力枚举就行
如果\(k\)比较小的话就用dp去进行状态转移,处理出来查询。


#include <bits/stdc++.h> 
#define int long long
#define rep(i,a,b) for(int i = (a); i <= (b); ++i)
#define fep(i,a,b) for(int i = (a); i >= (b); --i)
#define pii pair<int, int>
#define pll pair<long long, long long>
#define ll long long
#define db double
#define endl '\n'
#define x first
#define y second
#define pb push_back

using namespace std;

int s[100010][330];
void solve()
{
	int n,q;cin>>n;
	vector<int>a(n+1);
	rep(i,1,n){
		cin>>a[i];
	}
	//预处理出来模数比较小的情况
	int m=sqrt(n);
	
	fep(i,n,1){
		rep(j,1,m){
			if(i+a[i]+j>n)	s[i][j]=1;
			else	s[i][j]=s[i+a[i]+j][j]+1;
		}
	}
	
	cin>>q;
	while(q--){
		int x,y;cin>>x>>y;
		//模数较小直接查询预处理的结果
		if(y<=m){
			cout<<s[x][y]<<endl;
		}else{
			//较大的话暴力去做
			int ans=0;
			while(x<=n){
				++ans;
				x+=a[x]+y;
			}
			cout<<ans<<endl;
		}
	}		
}
signed main(){
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
//   	freopen("1.in", "r", stdin);
  	int _;
//	cin>>_;
//	while(_--)
	solve();
	return 0;
}



F. Sum of Progression


这种一次跳几个的一般需要想到根号分治
如果没有前面的那几个系数/权值就是一道比较经典的题目有系数和权值的话需要考虑怎么处理
这里维护两个数组f和g数组
\(f\)数组表示权值前缀和数组但是这里需要每次跳d
\(g\)数组表示单纯前缀和数组
\(f[i][j]=...+\frac{s}{d}a[s]+(\frac{s}{d}+1)a[s+d]+(\frac{s}{d}+2)a[s+2*d]+...\)

\(g[i][j]=...+a[s]+a[s+d]+a[s+2*d]+...\)

我们要求的答案是
$ $


#include <bits/stdc++.h> 
#define int long long
#define rep(i,a,b) for(int i = (a); i <= (b); ++i)
#define fep(i,a,b) for(int i = (a); i >= (b); --i)
#define pii pair<int, int>
#define pll pair<long long, long long>
#define ll long long
#define db double
#define endl '\n'
#define x first
#define y second
#define pb push_back

using namespace std;

int f[330][100010],g[330][100010];

void solve()
{
	int n,q;cin>>n>>q;
	vector<int>a(n+1);
	rep(i,1,n){
		cin>>a[i];
	}
	//预处理出来模数比较小的情况
	int sq=sqrt(n);

	rep(i,1,sq){
		rep(j,1,n){
			f[i][j]=(j-i>=0?f[i][j-i]:0)+j/i*a[j];
			g[i][j]=(j-i>=0?g[i][j-i]:0)+a[j];
		}
	}
	
	while(q--){
		int s,d,k;cin>>s>>d>>k;
		//模数较小直接查询预处理的结果
		if(d<=sq){
			int ans=0;
			ans+=f[d][s+(k-1)*d]-(s-d>=0?f[d][s-d]:0);
			ans-=(s/d-1)*(g[d][s+(k-1)*d]-(s-d>=0?g[d][s-d]:0));
			cout<<ans<<' ';
		}else{
			//较大的话暴力去做
			int ans=0;
			for(int i=s,j=1;j<=k;i+=d,j++){
				ans+=j*a[i];
			}
			cout<<ans<<' ';
		}
	}
	cout<<endl;		
}
signed main(){
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
//   	freopen("1.in", "r", stdin);
  	int _;
	cin>>_;
	while(_--)
	solve();
	return 0;
}

标签:入门,int,rep,分治,cin,long,根号,define
From: https://www.cnblogs.com/cxy8/p/18009560

相关文章

  • 大数据入门
    大数据学习路线一、大数据处理流程        1.1数据收集        1.2数据存储        1.3数据分析        1.4数据应用        1.5其他框架二、学习路线        2.1语言基础        2.2Linux基础   ......
  • python入门教程详细从零基础入门到精通一站式解决方案
    前言众所周知,Python以优雅、简洁著称,入行门槛低,可以从事Linux运维、PythonWeb网站工程师python自动化测试、数据分析、人工智能等职位,薪资待遇呈上涨趋势,对于许多未曾涉足IT行业「小白」来说,深入地学习python是一件十分困难的事。我这个小萌新当时什么也不懂,就傻乎乎地开始了学......
  • 零基础入门Vue之画龙点睛——再探监测数据
    追忆上一节:零基础入门Vue之影分身之术——列表渲染&渲染原理浅析虽然我深知,大佬告诉我”先学应用层在了解底层,以应用层去理解底层“,但Vue的数据如何检测的我不得不去学否则,在写代码的时候,可能会出现我难以解释的bug对此,本篇文章,将记录我对Vue检测数据的理解对于Vue检测数据......
  • 【Flink入门修炼】1-2 Mac 搭建 Flink 源码阅读环境
    在后面学习Flink相关知识时,会深入源码探究其实现机制。因此,需要现在本地配置好源码阅读环境。本文搭建环境:MacM1(AppleSilicon)Java8IDEAFlink官方源码一、下载Flink源码github地址:https://github.com/apache/flink考虑到一些原因,github下载可能会极其缓慢,且大......
  • SQL数据库入门04:数据查询操作
      本文介绍基于MicrosoftSQLServer软件,实现数据库表中多种数据查询方法的具体操作。(数据库基础(四):数据查询)  系列文章中示例数据来源于《SQLServer实验指导(2005版)》一书。依据本系列文章的思想与对操作步骤、代码的详细解释,大家用自己手头的数据,可以将相关操作与分析过程......
  • 十八张图带你入门实时监控系统HertzBeat
    我们经常讲:研发人员有两只眼睛,一只是监控平台,另一只是日志平台。在对性能和高可用讲究的场景里,监控平台的重要性再怎么强调也不过分。这篇文章,我们聊聊开源实时监控告警系统HertzBeat赫兹跳动。1产品特色HertzBeat有两个非常鲜明的特色:强大的监控模版和无需Agent。1.1......
  • 零基础入门Vue之影分身之术——列表渲染&渲染原理浅析
    听我说从条件渲染那一篇,我学习到了如何用Vue对dom节点根据条件显示但单单有条件还不够啊,有时候数据是一大坨一大坨的数据,如果Vue不提供咱要么使用“v-html”要么就没办法实现v-html又感觉太low了,Vue提供了另外的指令更好的实现,那便是:列表渲染列表渲染:v-for简单的列表渲染......
  • 耗时一个月我问遍了身边的大佬,零基础自学Java的路线,适用程序员入门&进阶,Java学习路线,2
    作为一个有志于成为Java程序员的你,或许正处在技术生涯的起点,或许已经走过了入门的道路,期待跨越进阶的门槛?无论处于哪个阶段,一条明确的学习路线都至关重要,通过向众多行业大佬请教、反复探索和实践,总结出一套适用于零基础自学者大学四年Java学习路线,也同样适用于从初级到研发专家的学......
  • 【Flink入门修炼】1-1 为什么要学习 Flink?
    流处理和批处理是什么?什么是Flink?为什么要学习Flink?Flink有什么特点,能做什么?本文将为你解答以上问题。一、批处理和流处理早些年,大数据处理还主要为批处理,一般按天或小时定时处理数据,代表性的框架为MapReduce、Hive、Spark等。但是,传统批处理的问题也很快显现:实时性......
  • kettle从入门到精通 第四十课 kettle 增量同步(分钟/小时级)
     1、上一课我们学习了在数据量大的情况下的分页全量同步示例,本次我们一起学习下kettle增量全量同步。有些业务场景不需要实时数据,比如每N分钟抽取一次数据等。  2、kettle增量全量同步示例依然基于test数据库,从t1表增量同步数据到t2表,假定每N(这里的N可以根据业务场景自定......