TopCoder SRM 675 Div1 Problem 500 LimitedMemorySeries1(分块)

题意  给定一个长度不超过$5*10^{6}$的数列和不超过$100$个询问,每次询问这个数列第$k$小的数,返回所有询问的和

   内存限制很小,小到不能存下这个数列。(数列以种子的形式给出)

   时限$10s$,内存限制$13MB$

 

我自己YY的分治缩小答案上下界范围第三个样例要跑$90s$左右,果断放弃

根据题目给出的条件我们知道每一个数的范围都在$[0, 10^{9}+6]$里。

那么我们开一个大小为$32000$的数组,把$[0, 10^{9}+6]$分成$32000$个大小相同的块。

然后先遍历整个数列,求出每个块中有多少个数。

在询问的时候先确定当前要查询的那个数在哪个块里。

确定了那个块的位置之后,我们再遍历一遍数列,找到那些在这个块里的数,再把精确值求出来。

时间复杂度$O(qn)$

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)
#define MP		make_pair
#define fi		first
#define se		second

typedef long long LL;

const int N = 32000;

int block[N], c[N];

class LimitedMemorySeries1 {
	public:
		LL getSum(int n, int x0, int a, int b, vector<int> query){
			memset(block, 0, sizeof block);
			
			int x = x0;
			rep(i, 0, n - 1){
				block[x / N]++;
				x = (int)((x * (LL)a + b) % 1000000007);
			}

			LL sum = 0;
			for (auto q : query){
				int acum = 0;
				int p = -1;
				int before = 0;
				rep(i, 0, N - 1){
					if (acum <= q && q < acum + block[i]){
						p = i;
						before = acum;
						break;
					}
					acum += block[i];
				}

				memset(c, 0, sizeof c);

				x = x0;
				for (int i = 0; i < n; i++){
					if (p * N <= x && x < (p + 1) * N){
						c[x - p * N]++;
					}
					x = (int)((x * (LL)a + b) % 1000000007);
				}

				acum = before;
				int r = -1;
				for (int i = 0; i < N; i++){
					if (acum <= q && q < acum + c[i]){
                                                r = p * N + i;
                                                break;
                                        }
					acum += c[i];
				}
				sum += r;
			}

			return sum;
		}
};

  

原文地址:https://www.cnblogs.com/cxhscst2/p/8443557.html