題解 洛谷 P3396 【雜湊衝突】(根號分治)

折翼的小鳥先生發表於2020-12-03

根號分治

前言

本題是一道講解根號分治思想的論文題(然鵝我並沒有找到論文),正

如論文中所說,根號演算法——不僅是分塊,根號分治利用的思想和分塊像

似卻又不同,某一篇洛穀日報中說過,分塊演算法實質上是一種是通過分成

多塊後在每塊上打標記以實現快速區間修改,區間查詢的一種演算法。根號

分治與其思路相似,將原本若一次性解決時間複雜度很高的問題分塊去解

決來降低整體的時間複雜度。

例題

以本題舉例子雜湊衝突

本題作為論文的第一道題目,是一道很好的練習題,注意,本體給出的

\(value[i]\)\(i\) 在序列中出現的次數,不要把題讀錯了(一開始我就讀錯了)

我們首先閱讀題目,發現,無論是 \(O(n^2)\) 預處理, \(O(1)\) 查詢,還是在

查詢時直接\(O(n^2)\)獲取答案,都是 \(O(n^2)\) 的時間複雜度,我們考慮對

其進行優化,我們可以考慮將\(p\),也就是模數按根號分塊處理

對於 \(p<=\sqrt{q}\) 我們直接 \(O(n\sqrt{n})\) 進行預處理,

	for(int i=1;i<=n;i++){
		v=read();
		val[i]=v;
	}
	size=sqrt(n); 
	for(int i=1;i<=n;i++){
		for(int p=1;p<=size;p++){
			ans[p][i%p]+=val[i]; 
		}
	}

\(ans[p][i]\) 表示在 \(%p\) 後值為 \(i\)的數的個數

\(p>\sqrt{q}\) 的情況,

我們直接暴力得出答案,暴力得到答案的時間複雜度為 \(O(\sqrt{n})\)

	int an=val[y]; 
	for(int i=x+y;i<=n;i+=x){
		an+=val[i]; 
	}
	cout<<an<<endl;

\(an=val[y]\) 是為了處理 \(y%x=y\) \((y<x)\) 的情況,為什麼說我們暴力跳

答案時間複雜度是 \(O(\sqrt{n})\) 呢?我們當前的 \(p>\sqrt{n})\) 因為統

計答案時每一次要跳\(p\)個數,所以有貢獻的數一定小於 \(\frac{n} {p}\) 也就是 \(\sqrt{n}\) ,所以暴力得到答案的時間複雜度為 \(O(\sqrt{n})\)

每一次修改,我們只需要修改 \(p<=\sqrt{n}\) 的情況即可,時間複雜度也是 \(O(\sqrt{n})\)

	cin>>x>>y;
	int l=y-val[x];
	val[x]=y; 
	for(int p=1;p<=size;p++){
		ans[p][x%p]+=l;
	}

所以在本題,我們運用根號分治的想法,把時間複雜度由原本的\(O(n^2)\)

優化到了 \(O(n\sqrt{n})\) 從而解決本題。

莫名覺得根號分治挺像折半搜尋
,推薦一道練習題CF444D DZY Loves Strings
還是很有難度的

程式碼

放一下全部程式碼吧

#include<iostream>
#include<string>
#include<string>
#include<cstdio>
#include<cmath>
#define int long long
using namespace std;
const int maxn=3e5+10;
inline int read(){
	int ret=0;
	int f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')	
			f=-f;
		ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		ret=ret*10+(ch^'0');
		ch=getchar();
	}
	return ret*f;
}
int val[maxn];
int n,m;
int p;
int ans[2000][2000];
int size;
char a;
signed main(){
//	freopen("a.in","r",stdin);
	n=read();
	m=read();
	int v;
	for(int i=1;i<=n;i++){
		v=read();
		val[i]=v;//???????? 
	}
	size=sqrt(n);//???? 
	for(int i=1;i<=n;i++){
		for(int p=1;p<=size;p++){
			ans[p][i%p]+=val[i]; 
		}
	}
	int x,y;
	while(m--){
		cin>>a;
		if(a=='A'){
			x=read();
			y=read();
			if(x<=size){
				cout<<ans[x][y]<<endl;
			}
			else{
				int an=val[y]; 
				for(int i=x+y;i<=n;i+=x){
						an+=val[i]; 
				}
				cout<<an<<endl;
			} 
		}
		if(a=='C'){
			cin>>x>>y;
			int l=y-val[x];
			val[x]=y; 
			for(int p=1;p<=size;p++){
				ans[p][x%p]+=l;
			}
		}
	}
	return 0;
}

到這裡本題解就結束了

完結撒花!!

相關文章