CF-1373E

 ACM  构造  数码 󰈭 911字

其实是一道比较简单的暴力构造题(?)

但是因为没有往这方面去考虑所以就…

正如之前看到过 第一个造原子弹的国家才是真正厉害的 因为这是开辟一条从0到1的道路 后面的效仿者已知结果可行再去效仿就容易很多


这道题中最麻烦的地方在于如果产生进位会如何影响结果。

如果一个数末尾有连续k个9,那么对其加1后其数码之和会变化+1 - 9k

得知这个规律后就可以将$f(x) + f(x + 1) + …+f(x + k)$写成

$(k + 1) * f(x) + \frac{k * (k + 1)}{2} - 9 * cnt * k$

其中,$cnt$表示$f(x)…f(x +k)$其中会产生进位的数字的个数,$k$表示进位后会受到影响的9的个数。

当我们枚举n、k时,可以再枚举cnt(因为k已经给定,所以只有末尾数字dig影响cnt的值,因而枚举cnt即枚举dig)以及k,每次看是否存在一个$f(x)$满足等式条件。如果满足整除条件,就利用参数$cnt(dig)、k、f(x)$进行构造并更新答案。

每次构造先用$dig、k$填充低位,然后根据剩余的数码和构造高位。显然优先填够9,让总位数尽可能最小;当剩余数码不超过9时,直接填入该数码即可。有一个要注意的地方,因为枚举的$k$是末尾连续9的个数,所以当填充完$dig$和$k$个9后,下一位不能继续填9,而应该用8去填充作为分割。

cpp
 1const int maxn = 200;
 2const ll inf = 1ll << 62;
 3ll ans[maxn][15];
 4
 5vector<int> vec;
 6void make(int n, int k, int last, int num, int sum){
 7    sum -= last; sum -= 9 * num;
 8    vec.clear(); vec.push_back(last);
 9    while(num--) vec.push_back(9);
10    if(sum < 0) return;
11    
12    if(sum >= 8){
13        //作为分割符
14        vec.push_back(8);
15        sum -= 8;
16    }
17    while(sum > 9){
18        vec.push_back(9); sum -= 9;
19    }
20    if(sum) vec.push_back(sum);
21    
22    ll res = 0;
23    int top = (int)vec.size(); if(top > 60) return;
24    for(int i = top - 1; i >= 0; i--){
25        res *= 10;
26        res += vec[i];
27    }
28    ans[n][k] = min(ans[n][k], res);
29}
30
31void sove(){
32    const int len = 20;
33    for(int n = 1; n <= 150; n++){
34        for(int k = 0; k <= 9; k++){
35            //f[x] * (k + 1) + (1 + ... + k) - 9 * 数的个数 * 每个数中末尾连续9的个数 = n。
36            //其中k是给定的,数的个数通过调节最后一位来控制,9的个数直接枚举.
37            int now = n - (k + 1) * k / 2;
38            for(int dig = 0; dig <= 9; dig++){
39                for(int num = 0; num < len; num++){
40                    int cnt = max(0, dig + k - 10 + 1);
41                    if((now + 9 * (num + 1) * cnt) % (k + 1) == 0){
42                        make(n, k, dig, num, ((now + 9 * (num + 1) * cnt) / (k + 1)));
43                    }
44                }
45            }
46        }
47    }
48}
49
50int main(){
51    for(int i = 0; i < maxn; i++) for(int j = 0; j < 15; j++) ans[i][j] = inf;
52    sove();
53
54//    Fastin;
55    int t; scanf("%d", &t); while(t--){
56        int n, k; scanf("%d %d", &n, &k);
57        if(ans[n][k] - inf) printf("%lld\n", ans[n][k]);
58        else puts("-1");
59    }
60    return 0;
61}
嗨! 这里是 rqdmap 的个人博客, 我正关注 GNU/Linux 桌面系统, Linux 内核 以及一切有趣的计算机技术! 希望我的内容能对你有所帮助~
如果你遇到了任何问题, 包括但不限于: 博客内容说明不清楚或错误; 样式版面混乱; 加密博客访问请求等问题, 请通过邮箱 rqdmap@gmail.com 联系我!
修改日志
  • 2023-09-01 18:14:49 单独划分ACM专题; 移动部分博客进入黑洞归档
  • 2023-05-29 23:05:14 博客结构与操作脚本重构
  • 2023-05-08 21:44:36 博客架构修改升级
  • 2022-11-16 01:27:34 迁移老博客文章内容