P8676 [蓝桥杯 2018 国 A] 自描述序列
zhanghao233 · · 题解
感谢 zhanghao 给我的帮助。
题目大意
-
自描述序列,记作
{G(n)} 。 -
对于任意正整数
n ,n 在整个序列中恰好出现G(n) 次。 -
这个序列是不下降的。
-
1 \le n \le 2\times 10^{15}
思路
1 暴力
最多卡到 40 分。
const int N=6e7+5;
a[0]=3,a[1]=1,a[2]=2,a[3]=2;
for(int i=3;i<=N;i++)
for(int j=1;j<=a[i];j++){
a[++a[0]]=i;
if(a[0]==N)
goto end;
}
end:cin>>n;
cout<<a[n];
2 优化
用范围来代表每个数的位置。
用二分查找出 没有其他优化的方案了
struct node{
int l,r,i;
}G[N];
int find_x(int x){
int mid=0,l=1,r=x;
while(l<=r){
mid=(l+r)/2;
if((G[mid].l<=x&&G[mid].r>=x)||l==r)
return G[mid].i;
if(G[mid].l>x)
r=mid-1;
if(G[mid].r<x)
l=mid+1;
}
}
for(int i=3;i<=N;i++){
G[i].l=l,G[i].r=r,G[i].i=i;
l=r+1,r=l+find_x(i+1)-1;
if(G[i].l<=n&&G[i].r>=n){
cout<<i<<"\n";
return 0;
}
}
3 正解
我们不妨扩大代表的范围。
对于每一个
- 对于
i 则会衍生出G_i 段长度相等的数据段。
注意:衍生和产生在文中不一样!产生是指
所以,我们可以确定
例:
因为
对于
推断:
因为数据段有
-
\lceil (n-m)/i \rceil
我们已经知道了
则我们只需要求出当
因为我们
答案为:
g[0]=2,g[1]=1,g[2]=2;
for(int i=2;i<=N;i++)
for(int j=1;j<=g[i];j++){
g[g[0]++]=i;
if(g[0]>=N-1)
goto end;
}
end: cin>>n;
int t=0,q=0;
for(int i=1;;i++){
t+=g[i]*i;
if(t>=n){
t-=g[i]*i;//ceil((n-t)*1.0/i) 我也不知道这样写为什么不可以
cout<<q+(n-t+i-1)/i<<"\n";
break;
}
q+=g[i];
}