1 条题解

  • 0
    @ 2025-11-26 21:08:47

    Written by @Yue_chen

    题意:

    给定一个 n 和 m,求[1 - n]中 有多少个数的阶乘末尾有m个0。

    思路:

    注意 n 和 m 很大,思维题 or 数位dp or 矩阵,很显然出题组不会考后面两个( )

    直接开玩,枚举发现,每当遇到一个5的倍数,就会多一个0。

    ok写完了直接交题。

    ok喜提 wa

    哦,不小心给0也算进去了,给0删了再交一发。

    ok喜提 wa

    肯定是我枚举的方法不对,重构一遍代码再交一发。

    ok喜提 wa

    如果你也红温了评论区请扣111

    后来我们发现,25的时候会多出两个0,很简单的道理,两个5,在前面激情wa的可能是小丑吧()

    回归正题。

    所以我们需要预处理5的所有幂数,然后使用二分去查找第一次出现m个0的数,锁定之后,向后查找5个数(为什么是5个数,因为更多的数一定会遇到新的5)。

    把这5个数都check一遍,合法的都加进答案里面。

    还有一个坑点,奶奶的这题卡常,给我十连评测都卡出来了,ok喜提tle,在我细节优化之后,也是成功通过了。

    时间复杂度O(log5(1e18) * log2n * T)

    AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    
    using i64 = long long;
    constexpr i64 N = 1e18;
    
    int cnt;
    i64 p5[1001];
    
    void solve() {
        i64 n, m; cin>>n>>m;
        
        auto check = [&](i64 x) {
            i64 ans = 0;
            for(int i=1; i<cnt; ++i) {
                i64 y = x/p5[i];
                if(y == 0) break;
                ans += y;
            }
            return ans;
        };
        
        i64 l = 0, r = n;
        while(r-l > 1) {
            i64 mid = l+r >> 1;
            if(check(mid) >= m) r = mid;
            else l = mid;
        }
        
        vector<i64> v;
        for(i64 i=r; i<=min(n, r+5); ++i) {
            if(check(i) == m) {
                v.push_back(i);
            }else {
                break;
            }
        }
        
        if(v.size() == 0) {
            cout<<"-1\n";
        }else {
            cout<<v.size()<<"\n";
            for(auto x: v) {
                cout<<x<<" ";
            } cout<<"\n";
        }
    }
    
    signed main(void) {
        ios::sync_with_stdio(false); cin.tie(nullptr);
        
        for(i64 i=1; N/i>=5; i*=5) {
            p5[cnt++] = i;
        }
        
        int t=1; cin>>t;
        while (t--) solve();
        return 0;
    }
    

    信息

    ID
    120
    时间
    2000ms
    内存
    256MiB
    难度
    9
    标签
    递交数
    101
    已通过
    5
    上传者