2024HBCPC:J Iris‘ Food

题目描述

猫猫 Iris 又在玩 oql 的键盘了。
T 天的时间里,每一天 Iris 会在玩 oql 键盘的时候敲下若干个字符,而这些字符恰好全都是 0 ∼ 9 0∼9 09 10 10 10 个阿拉伯数字。经过统计,数字 i i i a i a_i ai个。oql 每天可能会给 Iris 喂一定数量的猫粮。他决定,在这 ∑ i = 0 9 a i \sum^9_{i=0}a_i i=09ai 个数字里选择 m m m 个,经过重新排列后形成一个十进制下 m m m 位、且不包含前导 0 的非负整数 x,然后 oql 将在这天给 Iris 投喂 x x x 克的猫粮。
然而,Iris 还是一只小猫,不宜食用太多的猫粮。因此 oql 想让这个数字尽可能小。请你帮助 Iris 计算出她每天能得到多少猫粮。由于答案可能很大,你只需要输出答案对 1 0 9 + 7 10^9+7 109+7 取模的结果。请注意,你需要输出的是最小答案模 1 0 9 + 7 10^9+7 109+7 后的结果,而不是 x x x m o d mod mod 1 0 9 + 7 10^9+7 109+7 的最小值。

Input

第一行一个正整数 T ( 1 ≤ T ≤ 1 0 4 ) T (1≤T≤10^4) T(1T104),表示天数。
2 ∼ ( T + 1 ) 2∼(T+1) 2(T+1) 行,每行 11 11 11 个由空格分隔的非负整数 m , a 0 , a 1 , ⋯ , a 9 ( 1 ≤ m ≤ 1 0 9 , 0 ≤ a i ≤ 1 0 9 ) m,a_0,a_1,⋯,a_9(1≤m≤10^9,0≤a_i≤10^9) m,a0,a1,,a9(1m109,0ai109),表示第 i i i 天的情况。
数据保证有解,即至少能形成一个 m m m 位不包含前导 0 0 0 的非负整数。

Output

对于每一天,输出一行一个整数,表示 Iris 这一天能得到的猫粮对 1 0 9 + 7 10^9+7 109+7 取模后的结果。

Sample Input 1

3
3 1 0 0 0 3 0 0 0 0 0
1 2 0 0 0 0 0 0 0 0 0
4 0 1 1 1 3 0 0 0 0 0

Sample Output 1

404
0
1234

解题思路

很简单的一个贪心,因为不能有前导零,所以可以先从 1 ∼ 9 1 \sim 9 19 中拿出一个最小的数作为第一个数,之后就可以从 0 ∼ 9 0 \sim 9 09 从小往大依次选择 m − 1 m-1 m1 个数即可

接下来的问题就是高精度取余了,由同余定理可知:

假设一个数 x = a b c d = a ∗ 1000 + b ∗ 100 + c ∗ 10 + d x=abcd=a*1000+b*100+c*10+d x=abcd=a1000+b100+c10+d
= ( ( ( a ∗ 10 + b ) ∗ 10 + c ) ∗ 10 + d ) =(((a*10+b)*10+c)*10+d) =(((a10+b)10+c)10+d)
那么 x % p = ( ( ( ( ( ( a % p ) ∗ 10 + b ) % p ) ∗ 10 + c ) % p ) ∗ 10 + d ) % p x\%p=((((((a\%p)*10+b)\%p)*10+c)\%p)*10+d)\%p x%p=((((((a%p)10+b)%p)10+c)%p)10+d)%p

所以,对于一个数字字符串 S S S,求它取余 p p p,就是如下实现

long long res = 0;
for (char c : S)
{
    int x = c - '0';
    res = (res * 10 + x) % p;
}
return res;

那么,如何计算答案呢?
假设 a = x x x x x x a=xxxxxx a=xxxxxx,如何求 b = x x x x x x y y y y y y y y   m o d   p b=xxxxxxyyyyyyyy\,mod\,p b=xxxxxxyyyyyyyymodp 的值
我们可得到下列式子

b = a ∗ 1 0 8 + y y y y y y y y b = a * 10^8 + yyyyyyyy b=a108+yyyyyyyy
b % p = ( a ∗ 1 0 8 % p + y y y y y y y y % p ) % p b\%p=(a*10^8\%p+yyyyyyyy\%p)\%p b%p=(a108%p+yyyyyyyy%p)%p

现在,问题都解决了,我们只需要预处理出一个数组 c n t cnt cnt c n t [ i ] cnt[i] cnt[i]表示长度为 i i i 的全 1 1 1 数字 m o d   1 0 9 + 7 mod\,10^9+7 mod109+7 的值,上面的式子就可以得到以下变换:

b % p = ( a ∗ 1 0 8 % p + ( 11111111 ∗ y ) % p ) % p b\%p=(a*10^8\%p+(11111111*y)\%p)\%p b%p=(a108%p+(11111111y)%p)%p

最后,注意特判 m = 1 m=1 m=1 a [ 0 ] ≥ 1 a[0] \geq 1 a[0]1时,答案为 0 0 0

实现代码

#pragma GCC optimize(3, "Ofast", "inline")
#include<bits/stdc++.h>
#define int long long
#define x first
#define y second
#define debug(x) cerr << #x" = " << x << '\n';
using namespace std;

const int mod = 1e9 + 7;
int cnt[10000010];

int qmi(int a, int b)
{
    int res = 1;
    while (b)
    {
        if (b & 1) res = res * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return res;
}

void init()
{
    for (int i = 1; i <= 10000000; i++) // 预处理出10^7个1 % p的值
    {
        cnt[i] = cnt[i - 1] * 10 + 1;
        cnt[i] %= mod;
    }
}

void solve()
{
    int m;
    cin >> m;
    vector<int> a(10);
    for (int i = 0; i < 10; i++) cin >> a[i];
    int res = 0;
    if (m == 1 && a[0]) return cout << 0 << '\n', void();
    for (int i = 0; i < 10; i++)
    {
        if (a[i] && i)
        {
            res = res * 10 + i;
            res %= mod;
            m --;
            a[i] --;
            break;
        }
    }
    // 如果发现只能取0,则直接输出0
    if (res == 0) return cout << 0 << '\n', void();
    for (int i = 0; i < 10; i++)
    {
        int x = min(m, a[i]);
        m -= x;
        while (x >= 10000000)
        {
            res = (res * qmi(10, 10000000) % mod + cnt[10000000] * i % mod) % mod;
            x -= 10000000;
        }
        if (x) res = (res * qmi(10, x) % mod + cnt[x] * i % mod) % mod;
    }
    cout << res << '\n';
}

signed main()
{
    // freopen("Sample.in", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    init();
    int T = 1;
    cin >> T;
    while (T--) solve();
    return 0;
}
  • 22
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值