╮(╯▽╰)╭看到标题,想吐槽的各位大虾请手下留情,因为我的专长也就是直觉瞎搞了——说实话我都不知道怎么来的这么好运,居然猜出来了╮(╯▽╰)╭
满满的打表,233333
慢慢说吧。
定义两个符号:
d[i]
表示原序列的第i项
last(n)
表示(题目里定义的),在原始序列中最后一次出现n的位置的下标(显然题目要求变成,求
last(last(n))
)
然后,草稿纸上随便写写,显然有:
last(n)=∑i=1nd[i]
本来计划的是,写一个能够快速计算n很大的时候的 last(n) ,于是我选择打表看看:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <algorithm>
using namespace std;
typedef long long ll;
int d[500005];
int main(){
freopen("03table.txt","w",stdout);
d[1]=1;
d[2]=2;
d[3]=2;
int lastp=3;
for(int i=3;lastp<=500000;i++){
for(int j=0;j<d[i]&&lastp<=500000;j++){
d[++lastp]=i;
}
}
ll sum=0;
for(int i=1;i<=500000;i++){
sum+=d[i];
sum%=1000000007;
printf("%d %d %I64d\n",i,d[i],sum);
}
return 0;
}
然后我也回想不起当时想的是什么了(好像是分段存下d[i]为2、3、4…..的值的位置能产生的数的最后一个位置?反正是个冷静下来很没逻辑的行为……),于是,我把
sum+=d[i];
改写成:
sum+=d[i]*i;
然后打开表一看,好像我求出
last(last(n))
了?
去洗手间冷静了一下,回来验算了,好像是真的!
于是我们现在有个全新目标,把
last(last(n))
通过打表的方法,完成计算。
首先要解决一个问题:我们要知道
d[i]
,i 的范围是 10亿,这样的int数组爆内存了,开不下啊……
幸好我们有:
last(n)=∑i=1nd[i]
。于是,借助d[i]的前缀和,我们可以对每个n,用二分查找定位出其
d[n]
,之后往上找也不需要每次重新二分,直接做个推断就行。
然后,
d[n]
要预先算出多少才够呢?继续打表,找到
last(n)≥109
的n值,很高兴的发现,
n=500000
的时候就足够了。
当然,
n≤109
的情况下,全部打表很不现实,经典打表方案:分块打表——每隔d个,输出一个答案,之后每次查询期望复杂度
d/2
。
兴高采烈的写出如下打表程序:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN=500000;
int d[MAXN+5];
int sum[MAXN+5];
int main(){
freopen("03tablecode.txt","w",stdout);
printf("0,");
d[1]=1;
d[2]=2;
d[3]=2;
int lastp=3;
for(int i=3;lastp<=MAXN;i++){
for(int j=0;j<d[i]&&lastp<=MAXN;j++){
d[++lastp]=i;
}
}
for(int i=1;i<=MAXN;i++)
sum[i]+=sum[i-1]+d[i];
int p=1;
ll presum=0;
for(int i=1;i<=1000000000;i++){
while(sum[p]<i)p++;
presum+=(ll)p*(ll)i;
presum%=1000000007;
if(i%200000==0)printf("%I64d,",presum);
}
return 0;
}
注意,这里每隔10万项打一个值太多了,100KB的表不能提交;每隔20万项打一个值,表小了一半,可以顺利提交了。
然后把表往和上面的打表程序类似的代码里一贴,感觉期望复杂度可能运气好能过?一提交就TLE了……
然后仔细观察发现,其实我们并没有充分利用
d[i]
的前缀和
sum[i]
,已知
n
和其对应的
以下是代码:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXDN=500000;
int d[MAXDN+5];
int table[5005]={};//表太大了,这里略去
void init(){
d[1]=1;
d[2]=2;
d[3]=2;
int lastp=3;
for(int i=3;lastp<=MAXDN;i++){
for(int j=0;j<d[i]&&lastp<=MAXDN;j++){
d[++lastp]=i;
}
}
for(int i=2;i<=MAXDN;i++)
d[i]+=d[i-1];
}
int main(){
init();
int T,n;
for(scanf("%d",&T);T--;){
scanf("%d",&n);
int goal=n/200000*200000;
int p=lower_bound(d+1,d+MAXDN+1,goal)-d;
int presum=table[n/200000];
for(int i=goal+1;i<=n;i++){
while(d[p]<i)p++;
//等差数列求和
int obs=(min(d[p],n)-i+1);
int fe=i+min(d[p],n);
ll tmp=(ll)fe*obs/2;
tmp%=1000000007;
//乘相应的d[i]并取模,跳到下一段
presum+=(1LL*p*tmp)%1000000007;
if(presum>1000000007)presum-=1000000007;
i=d[p]; p++;
}
printf("%d\n",presum);
}
return 0;
}