前言:
这道题当时看的时候看错了,或者说看得太快,推了一下发现1是1个,2是4个,3就理所当然的认为是9个,然后飞速写完,检查都没检查出来,我也是服了自己。。。说实话确实很伤心,这道题就算是小白,写个暴力,50多分到手,我居然1分都没拿到,优秀奖滚粗也是理所应当的吧。
题目描述:
给一个数列,每一个元素都在G[N]数列中出现G[N]次。
i: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
G[i]: 1 2 2 3 3 4 4 4 5 5 5 6 6 6 6 7 7 7 7 8
数据规模:
最大为1<N<2*10^16(记不清了,反正在long long范围内)
输入样例:
13
输出样例:
6
解题思路:
这道题由于规模太大,用暴力只能过70%,还要考虑到空间问题。所以我们考虑直接求出N的G[N]肯定会超时。而G[N]可以通过G[G[N]]来求出,所以我们只需要先计算出最近的G[G[N]],每次计算保存N-1的G[N-1],在通过模除G[G[N]]来得出最终的结果。
注意:当所更新的数值的G[G[N]]可以包括的范围大于N或小于0(不知道会不会出现,反正加上)时就不再继续更新,这样优化可以节省极大的时间,测试了10^16的G[N]是9296812705,而9296812705的G[N]是1740381,所以应该是可以过的。所有的数据均采用long long int保存。
源代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#define INF 99999999
#define bug puts("Hello\n")
using namespace std;
int a[10000000];
int main(){
long long int N;
scanf("%lld",&N);
a[1]=1;
a[2]=2;
long long int i=0,flag=1,tflag=1,cflag=0,pre=1,temp=0,ctemp=0;
while(1){
if(temp+a[flag]*flag>N||temp+a[flag]*flag<0)break;
temp+=a[flag]*flag;
// printf("%lld\n",temp);
if(cflag==0)
for(long long int i=0;i<a[flag];i++){
if(cflag==0)
for(long long int j=0;j<flag;j++){
a[pre]=tflag+i;
ctemp+=a[pre]*pre;
// printf("%lld %lld %lld\n",pre,tflag+i,ctemp);
pre++;
if(ctemp+a[pre]*pre>N||ctemp+a[pre]*pre<0){
cflag=1;
break;
}
}
}
tflag+=a[flag];
flag++;
// printf("flag:%lld tflag:%lld\n",flag,tflag);
}
long long int now=N-temp,ans;
// printf("%d\n",now);
ans=now/flag+tflag;
if(now%flag==0)ans--;
printf("%lld\n",ans);
return 0;
}