题目链接:
题目描述:
刚开始给一个1,序列a是由a[i]个i组成,最后1就变成了1,2,2,3,3,4,4,4,5,5,5.......,最后问a[i]出现n次(i最大)时候,i最后一次出现的下标是多少?
解题思路:
问题可以转化为求a[i] == n (i最大),数列前i项的和为多少。
index: 1 2 3 4 5 6 7 8 9 10
a: 1 2 2 3 3 4 4 4 5 5
可以观察出:ans[1] = 1, ans[i] = ans[i-1] + a[i]*i;
但是n<=1e9,打不了这个庞大的表。进一步观察,可以发现a数组里面有很多的重复元素,辣么就可以对a数组里面的元素进行压缩存进b数组里面(出现次数为i的最后一个元素为b[i]):
index: 1 2 3 4 5 6 7 8 9 10
a: 1 2 2 3 3 4 4 4 5 5
b: 1 3 5 8 11 15 19 23 28 33
ans: 1 11 38 122 272 596 1086
1到出现次数为i的最后一个元素的区间和为ans[i],由a[]可以看出ans[i] = ans[i-1] + (b[i] + b[i-1] + 1) * (b[i] - b[i-1]) / 2 * i;
每次查询的时候如果n不在b[i]里面,可以找到一个最接近n并且小于n的b[i],然后再套用一次等差数列求和即可。
还有就是等差数列求和的时候,除以2要用到逆元处理一下。
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 using namespace std; 6 7 typedef __int64 LL; 8 const int INF = 0x3f3f3f3f; 9 const int maxn = 450000; 10 const int mod = 1000000007; 11 LL a[maxn], b[maxn], ans[maxn], num; 12 13 LL quick_mod(LL x, LL n) 14 { 15 LL res = 1; 16 17 while (n) 18 { 19 if (n % 2) 20 res = (res * x) % mod; 21 22 x = (x * x) % mod; 23 n /= 2; 24 } 25 return res % mod; 26 } 27 void init () 28 { 29 LL res = 1, j = 1, nu; 30 31 a[0] = b[0] = 0; 32 a[1] = b[1] = 1; 33 a[2] = num = 2; 34 b[2] = 3; 35 36 while (b[num] <= 1e9) 37 { 38 if (res <= num) 39 { 40 j ++; 41 res += a[j]; 42 } 43 44 while (res > num) 45 { 46 num ++; 47 a[num] = j; 48 b[num] = b[num-1] + a[num]; 49 } 50 51 } 52 53 //printf ("%I64d %I64d ", num, b[num]); 54 55 ans[0] = 0; 56 ans[1] = 1; 57 58 for (int i=2; i<=num; i++) 59 ans [i] = (ans[i-1] + (b[i] + b[i-1] + 1) % mod * a[i] % mod * i % mod * quick_mod(2, mod-2) % mod) % mod; 60 61 } 62 63 int main () 64 { 65 LL t, n; 66 init (); 67 scanf ("%I64d", &t); 68 69 while (t --) 70 { 71 scanf ("%I64d", &n); 72 LL pos = lower_bound (b, b+num, n) - b; 73 74 if (b[pos] == n) 75 { 76 printf ("%I64d ", ans[pos]); 77 continue; 78 } 79 80 LL res = (ans[pos-1] + (b[pos-1] + n + 1) % mod * (n - b[pos-1]) % mod * pos % mod * quick_mod(2, mod-2) % mod) % mod; 81 82 printf ("%I64d ", res); 83 } 84 return 0; 85 }