[LOJ#2328]「清华集训 2017」避难所

Posted xjr_01

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[LOJ#2328]「清华集训 2017」避难所相关的知识,希望对你有一定的参考价值。

[LOJ#2328]「清华集训 2017」避难所

试题描述

“B君啊,你当年的伙伴都不在北京了,为什么你还在北京呢?”

“大概是因为出了一些事故吧,否则这道题就不叫避难所了。”

“唔,那你之后会去哪呢?”

“去一个没有冬天的地方。”

对于一个正整数 \(n\),我们定义他在 \(b\) 进制下,各个位上的数的乘积为 \(p = F(n, b)\)

比如 \(F(3338, 10) = 216\)

考虑这样一个问题,已知 \(p\)\(b\),求最小的 \(n\) 满足 \(p = F(n, b)\)

这是一个非常有趣的问题,对于一些 \(b\) 来说,我们可以贪心来做,比如如果 \(b=10, p=216\)

我们可以从 \(b-1\)\(2\) 试除,直到 \(p\)\(1\) 为止,答案是 \(389\),可以验证 \(389\) 是满足 \(p = F(n, b)\) 最小的 \(n\)

但是对于一些进制 \(b\),是不能用贪心做的,比如 \(b = 9, p = 216\)。使用贪心得到的解是 \(3338\),而最优解是 \(666\)。(均为 \(9\) 进制下的。)

本题便是在给定进制 \(b\) 的情况下,举出一个这样的反例,或指出这样的反例不存在。

由于计算资源所限,反例中所有数字的乘积不能超过 \(10^{18}\)。如果最小的反例中所有数字的乘积超过了 \(10^{18}\),那么也应该输出 \(-1\)

输入

从标准输入读入数据。

第一行一个整数 \(t\),表示一共有 \(t\) 组数据。

接下来每行一个整数 \(b\),表示进制。

输出

输出到标准输出。

如果不存在反例,输出一行一个整数 \(-1\)

如果存在反例,首先输出一个整数 \(k\),表示反例 \(n\) 的位数,接下来在同一行输出 \(k\) 个十进制整数,表示任意一个反例的最优解。

输入示例

3
8
9
10

输出示例

-1
3 6 6 6
-1

数据规模及约定

对于第 \(1\) 个测试点,分值为 \(30\)\(1 \leq b \leq 32\)

对于第 \(2\) 个测试点,分值为 \(40\)\(1 \leq b \leq 100\)

对于第 \(3\) 个测试点,分值为 \(30\)\(1 \leq t \leq 200, 1 \leq n \leq 100000\)

题解

这题。。。woc居然是个打表。。。

当然只有 \(b \le 130\) 的时候需要打表,剩下的可以构造。

\(b > 130\) 时,实验证明都可以用三个相同的数字举出反例,令这相同的三个数字值为 \(p_1p_2\)(这是两个数的乘积),那么需要让他的贪心翻车,就要想方设法让贪心搞出 \(4\) 位数来,即 \(p_2, p_2, p_2, p_1^3\) 这样的一个四位数。

那么只需要满足几个条件:

  • \(p_1, p_2\) 都是素数

  • \(p_1p_2 < b\)

  • \(p_1^3 < b\)

  • \(p_2^2 \ge b\)

  • \(p_1p_2 < p_1^3\)

下面是打表程序:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)

int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
    return x * f;
}

#define maxn 100010
#define LL long long

int prime[maxn], cp;
bool vis[maxn];
void init() {
    rep(i, 2, maxn - 1) {
        if(!vis[i]) prime[++cp] = i;
        for(int j = 1; j <= cp && i * prime[j] < maxn; j++) {
            vis[i*prime[j]] = 1;
            if(i % prime[j] == 0) break;
        }
    }
    return ;
}

int ans, now[maxn], sol[maxn];
void dfs(int up, int x, int cur) {
    if(ans <= cur) return ;
    if(x == 1){ ans = cur; rep(i, 1, cur) sol[i] = now[i]; return ; }
    dwn(i, up, 2) if(x % i == 0) now[cur+1] = i, dfs(i, x / i, cur + 1);
    return ;
}

int main() {
    freopen("list.txt", "w", stdout);
    
    init();
    
    int reach = 140;
    printf("char Ans[%d][50] = {", reach);
    rep(b, 1, reach) {
        int x = 1; bool ok = 0;
        while(x <= 1000000) {
            int tmp = ++x;
            // greedy
            int cnt = 0;
            dwn(i, b - 1, 2) if(tmp % i == 0) tmp /= i, i++, cnt++;
            // force
            ans = cnt;
            dfs(b - 1, x, 0);
            if(ans < cnt) {
                sort(sol + 1, sol + ans + 1);
                printf("\"%d ", ans);
                rep(i, 1, ans) printf("%d%c", sol[i], i < ans ? ‘ ‘ : \");
                ok = 1;
                break;
            }
        }
        if(!ok) printf("\"-1\"");
        printf("%s", b < reach ? ", " : "};\n");
    }
    
    return 0;
}

下面是 AC 程序:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)

int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
    return x * f;
}

#define maxn 100010
#define LL long long

char Ans[140][50] = {"-1", "-1", "-1", "-1", "-1", "-1", "-1", "-1", "3 6 6 6", "-1", "-1", "-1", "3 9 10 10", "3 9 10 10", "3 9 10 10", "-1", "3 12 14 14", "3 12 14 14", "3 10 15 15", "3 10 15 15", "3 10 15 15", "3 10 15 15", "3 10 15 15", "3 10 15 15", "3 10 15 15", "3 15 18 18", "3 15 18 18", "3 14 20 21", "3 15 21 21", "3 15 21 21", "3 15 21 21", "3 15 21 21", "3 15 21 21", "3 15 21 21", "3 15 21 21", "3 21 21 21", "3 21 21 21", "3 21 21 21", "3 21 21 21", "3 21 21 21", "3 21 21 21", "3 21 21 21", "3 21 21 21", "3 21 21 21", "3 21 21 21", "3 21 21 21", "3 21 21 21", "3 21 21 21", "3 21 21 21", "3 22 33 35", "3 22 33 35", "3 22 33 35", "3 22 33 35", "3 22 33 35", "3 22 33 35", "3 26 35 39", "3 26 35 39", "3 26 35 39", "3 26 35 39", "3 26 35 39", "3 26 35 39", "3 26 35 39", "3 26 35 39", "3 26 35 39", "3 26 35 39", "3 22 44 49", "3 22 44 49", "3 22 44 49", "3 22 44 49", "3 22 44 49", "3 22 44 49", "3 22 44 49", "3 22 44 49", "3 22 44 49", "3 22 44 49", "3 22 44 49", "3 22 44 49", "3 26 49 52", "3 26 49 52", "3 26 49 52", "3 26 49 52", "3 26 49 52", "3 26 49 52", "3 26 49 52", "3 26 49 52", "3 26 49 52", "3 26 49 52", "3 26 49 52", "3 26 49 52", "3 26 49 52", "3 26 49 52", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 55 55", "3 33 65 65", "3 33 65 65", "3 33 65 65", "3 33 65 65", "3 33 65 65", "3 33 65 65", "3 33 65 65", "3 33 65 65", "3 33 65 65", "3 33 65 65", "3 33 65 65", "3 33 65 65", "3 33 65 65", "3 33 65 65", "3 33 65 65", "3 33 65 65", "3 33 65 65", "3 33 65 65", "3 33 65 65"};

int prime[maxn], cp;
bool vis[maxn];
void init() {
    rep(i, 2, maxn - 1) {
        if(!vis[i]) prime[++cp] = i;
        for(int j = 1; j <= cp && i * prime[j] < maxn; j++) {
            vis[i*prime[j]] = 1;
            if(i % prime[j] == 0) break;
        }
    }
    return ;
}

int main() {
    init();
    
    int T = read();
    while(T--) {
        int b = read();
        if(b > 140) {
            bool ok = 0;
            for(int i = 1; i <= cp && prime[i] <= b; i++) {
                for(int j = 1; j <= cp && prime[j] <= b; j++) {
                    int p1 = prime[i], p2 = prime[j];
                    if((LL)p1 * p2 < b && (LL)p1 * p1 * p1 < b && (LL)p2 * p2 >= b && (LL)p1 * p1 > p2) {
                        printf("3 %lld %lld %lld\n", (LL)p1 * p2, (LL)p1 * p2, (LL)p1 * p2);
                        ok = 1; break;
                    }
                }
                if(ok) break;
            }
            if(!ok) puts("-1");
        }
        else puts(Ans[b-1]);
    }
    
    return 0;
}

以上是关于[LOJ#2328]「清华集训 2017」避难所的主要内容,如果未能解决你的问题,请参考以下文章

[LOJ#2327]「清华集训 2017」福若格斯

[LOJ#2325]「清华集训 2017」小Y和恐怖的奴隶主

@loj - 2320@ 「清华集训 2017」生成树计数

loj2322 「清华集训 2017」Hello world!

Loj #6703 -「清华集训 2017」生成树计数

*LOJ#2322. 「清华集训 2017」Hello world!