题目链接:https://vjudge.net/problem/HDU-5439
Aggregated Counting
Aggregated Counting Meetup (ACM) is a regular event hosted by Intercontinental Crazily Passionate Counters (ICPC). The ICPC people recently proposed an interesting sequence at ACM2016 and encountered a problem needed to be solved.
The sequence is generated by the following scheme.
1. First, write down 1, 2 on a paper.
2. The 2nd number is 2, write down 2 2’s (including the one originally on the paper). The paper thus has 1, 2, 2 written on it.
3. The 3rd number is 2, write down 2 3’s. 1, 2, 2, 3, 3 is now shown on the paper.
4. The 4th number is 3, write down 3 4’s. 1, 2, 2, 3, 3, 4, 4, 4 is now shown on the paper.
5. The procedure continues indefinitely as you can imagine. 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, . . . .
The ICPC is widely renowned for its counting ability. At ACM2016, they came up with all sorts of intriguing problems in regard to this sequence, and here is one: Given a positive number nn, First of all, find out the position of the last nn that appeared in the sequence. For instance, the position of the last 3 is 5, the position of the last 4 is 8. After obtaining the position, do the same again: Find out the position of the last (position number). For instance, the position of the last 3 is 5, and the position of the last 5 is 11. ICPC would like you to help them tackle such problems efficiently.
Input
The first line contains a positive integer T,T≤2000, indicating the number of queries to follow. Each of the following T lines contain a positive number n(n≤109)representing a query.
Output
Output the last position of the last position of each query n. In case the answer is greater than 1000000006, please modulo the answer with 1000000007.
Sample Input
3 3 10 100000
Sample Output
11 217 507231491
题目大意:题意很简单,让你求 给定一个n,找到n出现的最后位置p,再找p出现的最后位置。
题目思路:
先定义两个数组 g[]和f[]。
g[i]表示i连续出现了几次,即i位置上放的数。
f[i]表示数字i最后出现的位置。
后面去计算g[i]的时候就可以考虑的是找到第 i 个位置在那个f[]的区间内 , 如果f[k-1]< i <= f[k]
那么说明此时 g[i] = k
那么就可以logn的复杂度计算g[n]了
要计算最后的答案,要考虑的是,给定的n,找到最后出现的p,中间长度 p = f[p] = g[1]+g[2]....+g[p]
然后再找对应的ans ,那么每次增加的g[i],就会让整个序列 的长度增加 i*g[i]
i*g[i] 可以理解为的是,长度为i的数量有g[i]个, 所以总长度是i*g[i]
所以ans = sigma(i*g[i]) i<=n
那么对于n <= 1e9
那么大致计算一下会发现f[500000]>1e9
所以g[n]<500000只要暴力求出前500000的g[] , f[]
那么答案计算前,先找到g[n]是多少
g[n]= lower_bound(f+1 , f+N+1 , n)-f
然后说明[1 , g[n]-1]这一段区间内的所有长度都被用到了
所以之前预处理这个长度的前缀和 sum[]
对于每一个长度 i ,他出现的次数都是 f[i]-f[i-1]
sigma(n*g[n]) f[i-1]<n<=f[i] -> g[n] = i
那么答案就是 i*等差数列了,记得取模(⊙o⊙)哦
然后(g[n]-1 , g[n]]这一段只要枚举 (g[n]-1 , n] 就可以了
思路来自:https://www.cnblogs.com/CSU3901130321/p/4805973.html
标注一下sum[]数组的含义:
sum[i]表示出现次数为i的数的最后位置的最后位置。例如sum[2]=11(数字2和数字3的出现次数为2,3的最后位置是5,5的最后出现位置是11)
AC代码
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <map> #include <set> #include <stack> #include <queue> #include <vector> #include <string> #define cla(a, sum) memset(a, sum, sizeof(a)) #define rap(i, m, n) for(i=m; i<=n; i++) #define rep(i, m, n) for(i=m; i>=n; i--) using namespace std; typedef long long ll; typedef pair<ll, ll> P; const int Inf = 0x3f3f3f3f; const double eps = 1e-8; const int maxn = 5e5+5; const int MOD = 1000000007; ll T,n; ll f[maxn],g[maxn]; ll sum[maxn]; void init() { f[1]=1;f[2]=3; g[1]=1;g[2]=2; ll i,j,k; for(i=3;i<=5e5;i++){ j=lower_bound(f+1,f+i,i)-f; g[i]=j; f[i]=f[i-1]+g[i]; } sum[1]=1; for(i=2;i<=5e5;i++){ sum[i]=sum[i-1]+(f[i]+f[i-1]+1)*g[i]/2%MOD*i%MOD; sum[i]=sum[i]%MOD; } } int main() { init(); cin>>T; while(T--){ cin>>n; ll pos=lower_bound(f+1,f+maxn-1,n)-f; ll ans=sum[pos-1]; ll i,j; for(i=f[pos-1]+1;i<=n;i++){ ans=(ans+pos*i%MOD)%MOD; } cout<<ans<<endl; } return 0; }