hdu 5439 Aggregated Counting(规律)

题目链接:hdu 5439 Aggregated Counting


1 2 2 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8 8 8 8 .... 按照每个数出现的次数1出现了1次,2,3出现两次,以此类推,将次数取出作为序列,会发现对应的序列仍是 1 2 2 3 3 4 4 4 5 5 5 ....即原序列。第一次last我们可以根据前缀和求出,但是对于第二次last,由于第一次求完后的值会高达1e15,时间上肯定不允许利用前缀和去求解,但是前面讲到,按照每个数出现的次数形成新的序列是和原序列是相同的,那么假设我们考虑出现次数为4的数,(6, 7, 8),那么它们对应在序列中影响的位置长度为(6 + 7 + 8) * 4,而对应出现次数为4的数有几个,我们可以同过预处理打表得出。


#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long ll;
const int maxn = 500000;
const int mod = 1e9+7;

struct Seg {
	ll l, r, s;
	Seg(ll l = 0, ll r = 0, ll s = 0): l(l), r(r), s(s) {}
}seg[maxn];

int binsearch(int l, int r, int x) {
	while (l < r) {
		int mid = (l + r) >> 1;
		if (x > seg[mid].r) l = mid + 1;
		else r = mid;;
	}
	return l;
}

ll get(ll l, ll r) {
	return (r-l+1)*(r+l) / 2 % mod;
}

void init () {
	seg[1] = Seg(1, 1, 1);
	seg[2] = Seg(2, 3, 11);

	for (int i = 3; i < maxn; i++) {
		int v = binsearch(1, i-1, i);
		ll l = seg[i-1].r+1, r = seg[i-1].r+v;
		seg[i] = Seg(l ,r, (seg[i-1].s + get(l, r) * i % mod) % mod);
	}
}

int solve (int n) {
	int k = binsearch(1, maxn-1, n);
	return (seg[k-1].s + get(seg[k-1].r+1, n) * k % mod) % mod;
}

int main () {
	init();

	int cas, n;
	scanf("%d", &cas);
	while (cas--) {
		scanf("%d", &n);
		printf("%d\n", solve(n));
	}
	return 0;
}


你可能感兴趣的:(hdu 5439 Aggregated Counting(规律))