1. 程式人生 > 實用技巧 >P 3396 雜湊衝突 根號分治

P 3396 雜湊衝突 根號分治

Link

據說這是一道論文題????。具體論文好像是 集訓隊論文《根號演算法——不只是分塊》

根號分治的裸題。

首先我們考慮暴力怎麼打。

  • 先預處理出每個模數的答案,之後再 O(1) 的回答,修改預處理O(\(n^2\))
  • 每次詢問直接暴力統計,修改是 O (1) 的,但回答是O(\(n^2\)) 的。

這兩種寫法都不能通過此題。

那我們想辦法把詢問和修改的複雜度均攤一下。

對於模數比較少的數,我們直接暴力統計的話,會涉及到的數比較多,這樣時間複雜度就上去了,所以我們採用方法一,來減少詢問的複雜度。

對於模數比較大·的數,我們就可以直接暴力回答,因為涉及到的數不會太多,這樣我們的複雜度是完全可以接受的。

我們一般把這個閾值設為 \(\sqrt{n}\) ,比這個數大的,我們認為他是比較大的模數,直接暴力統計答案的詢問最多涉及到 \({n \over {\sqrt n}} = \sqrt n\) 個數。

總的複雜度為 \(O((n+m)\sqrt n)\)

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define LL long long
int n,m,x,y,maxn,T,a[150010],f[400][400], ans;
inline int read()
{
	int s =  0,w = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
	return s * w;
}
int main(){
	n = read(); m = read(); T = sqrt(n);
	for(int i = 1; i <= n; i++)
	{
		a[i] = read();
		for(int p = 1; p <= T; p++)//模數比較小的數,先預處理出答案來
		{
			f[p][i % p] += a[i];
		}
	}
	for(int i = 1; i <= m; i++)
	{
		char opt; cin>>opt; 
		x = read(); y = read();
		if(opt == 'A')
		{
			if(x <= T) printf("%d\n",f[x][y]);//模數小的數可以直接回答
			else
			{
				ans = 0;
				for(int j = y; j <= n; j += x)//模數比較大的數暴力統計
				{
					ans += a[j];
				}
				printf("%d\n",ans);
			}
		}
		else if(opt == 'C')
		{
			for(int p = 1; p <= T; p++)
			{
				f[p][x % p] += y - a[x];//增量法對預處理的值修改
			}
			a[x] = y;
		}
	}
	return 0;
}