题解:关注洛谷 small_lemon_qwq 谢谢喵

洛谷 P1662。


打表,我和你拼了!

正解,我和你拼了!


众所周知,打表是一种非常优秀的算法,只是有以下缺点:

  • 无法提交(本题可以用分段打表,没有这个问题);
  • 被骂。

所以我们为了不被骂,只能另寻他法。

n = X n=X n=X

注意到,从 70 70 70 报数报到 79 79 79,每报一次数报数的方向就反一下,那么实际相当于报数的方向不变,并且报 80 80 80 的人就是报 70 70 70 的人,所以可以考虑直接将其优化掉,遇到非个位出现 7 7 7 直接将它变成 8 8 8,但是注意变成 8 8 8 不要超过了 n n n

这样时间复杂度就是了 O ⁡ ( 9 8 × 10 × 9 ) = O ⁡ ( 3874204890 ) ≈ O ⁡ ( 4 × 10 9 ) \operatorname{O}(9^8\times10\times9)=\operatorname{O}(3874204890)\approx\operatorname{O}(4\times10^9) O(98×10×9)=O(3874204890)O(4×109),显然无法通过,考虑优化检查是否存在为 7 7 7 的位这一步。

这个题解启发我们可以用高精度动态求检查是否存在为 7 7 7 的位,但我们不仅要判断是否存在为 7 7 7 的位,还要求出这个为 7 7 7 的位。我的做法是开一个栈存储所有为 7 7 7 的位,动态更新这个栈,查询时直接返回栈顶(如果有)。

考虑计算时间复杂度, O ⁡ ( 9 8 × 10 + 111111111 ) = O ⁡ ( 541578321 ) ≈ O ⁡ ( 5 × 10 8 ) \operatorname{O}(9^8\times10+111111111)=\operatorname{O}(541578321)\approx\operatorname{O}(5\times10^8) O(98×10+111111111)=O(541578321)O(5×108),其中 111111111 111111111 111111111 是高精度进位次数,卡一下午常就过了。

注意 C++11 和 C++14(GCC 9) 的区别。

#include<bits/stdc++.h>
using namespace std;
int d=1,x;
unsigned n,i=1,num[10]={1};
constexpr unsigned p[10]={1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000};
unsigned stk[10],tp;
void update(){
    register int k=0;
    ++num[0];
    tp+=(num[0]==7)-(num[0]==8);
    if(__builtin_expect(num[0]==7,0))stk[tp]=0;
    while(__builtin_expect(num[k]>9,0)){
        num[k]-=10;
        num[++k]++;
        tp+=(num[k]==7)-(num[k]==8);
        if(__builtin_expect(num[k]==7,0))stk[tp]=k;
    }
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n;
    // if(n==899999999){
    // 	cout<<959;
    // 	return 0;
      // }//看清楚,我注释了的
	#ifndef ONLINE_JUDGE
	auto start=clock();
	#endif
	while(__builtin_expect(i<=n,1)){
		x+=d;
		register int tmp=__builtin_expect(tp,0)?stk[tp]:(__builtin_expect(i%7==0,0)-1);
		if(tmp!=-1)d=~d+1;
		if(__builtin_expect(tmp>0,0)){
			if(__builtin_expect(i+p[tmp]<=n,1)){
				i+=p[tmp];
				++num[tmp];
				--tp;
			}else{
				if((n-i)&1)x+=d;
				break;
			}
			x+=d;
			d=~d+1;
			continue;
		}
		++i,update();
	}
	cout<<(x%1337+1336)%1337+1;
	#ifndef ONLINE_JUDGE
	cout<<"\n"<<clock()-start;
	#endif
	return 0;
}

这个代码 C++14(GCC 9) 会 TLE,不知道为什么,很玄学。

这样在 n = 10 9 n=10^9 n=109 时要跑 1.1 s 1.1s 1.1s,所以请后人继续努力,或增大时限。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值