首页 > 其他 > 详细

Atcoder ABC161 小结

时间:2020-08-02 17:52:04      阅读:167      评论:0      收藏:0      [点我收藏+]

暴露的问题

  • 手速不够,简单题做太慢
  • 轻敌,以为 ABC 就很 Easy,结果 D 来了个恶心 dfs。话说感觉 D 比 F 还难?

D E F 题解题报告

D Lunlun Number

这道题的难度其实很大一部分取决于你的解法——你可以使用数位 dp 来解决此题,但是这就是大材小用了。一个更简单的方法是借用递归(也有点像 dfs),官方题解中的队列也可以解决本题。

递归解法:令 \(num[j]\) 表示第 \(j\) 位的数字,\(j\) 从小到大对应的是从低位到高位。

考虑第 \(j\) 位,当 \(num[j]=num[j+1]-1\) 时,下一个数字肯定就有 \(num[j]=num[j+1]\)

\(num[j]=num[j+1]\) 时,如果 \(num[j+1] ≠ 9\),下一个数字肯定有 \(num[j]=num[j+1]+1\)。如果 \(num[j+1]=9\),说明这两位为 \(99\),递归更高一位解决。

\(num[j]<num[j+1]\) 时,有两种情况。第一种情况是,\(j\) 非最高位,此时直接递归到更高位解决。第二种情况是,\(j\) 为最高位,按照题目规定,此时直接把 \(num[j]\)\(1\) 即可。

此外还有一点细节,请移步代码。

#include <iostream>
#include <cstring>
#include <cstdio>

#define LL long long

using namespace std;

inline LL read() {
    LL x = 0, f = 1;
    char c = getchar();
    while(c < ‘0‘ || c > ‘9‘) {
        if(c == ‘-‘) f = -1;
        c = getchar();
    }
    while(‘0‘ <= c && c <= ‘9‘) {
        x = x * 10 + c - ‘0‘;
        c = getchar();
    }
    return x * f;
}

int k, num[20], lim = 1;
bool vis[20];

void dfs(int j) {
    lim = max(lim, j);
    if(num[j] > num[j + 1]) {
        if(lim == j && num[j] < 9) ++num[j];
        else dfs(j + 1), num[j] = num[j + 1] ? num[j + 1] - 1 : 0;
    }
    else if(num[j] == num[j + 1] - 1) {num[j] = num[j + 1]; return;}
    else {
        if(num[j] < 9) {num[j] = num[j + 1] + 1; return;}
        else dfs(j + 1), num[j] = num[j + 1] ? num[j + 1] - 1 : 0;
    }
}

void output() {
    bool lead = 1;
    for(int j = 12; j; --j) {
        if(lead && !num[j]) continue;
        else {
            lead = 0;
            cout << num[j];
        }
    }
    cout << endl;
}

int main() {
    k = read();
    for(int i = 1; i <= k; ++i) dfs(1);
    output();
    return 0;
}

E Yutori

一道有一点思维含量的题目。考试时因为时间不够(都用来划水了),没有做这道题(当时看 F 题 AC 人数更多就先做的 F)。

Tag:贪心。其实这道题不是很难,问题是需要保持头脑清醒并理性转化问题。

考虑令 \(L[i]\) 表示前 \(i\) 天最多可以安排多少工作日,\(R[i]\) 表示后 \(i\) 天最多可以安排多少工作日。

这个 \(L[i]\)\(R[i]\) 可以显然地用贪心求出,细节可以康代码,这里便不再赘述。

对于第 \(i\) 天必须成为工作日的条件就是 \(L[i-1]+R[i+1]+1=K\),这个不难理解。这么想:前 \(i-1\) 天最多工作 \(L[i-1]\) 天,后 \(i+1\) 天最多工作 \(R[i+1]\) 天。这两边做到极致,如果都无法完成 \(K\) 天的任务,那么就必须让第 \(i\) 天参与工作。反之我们总有方案使得第 \(i\) 天不用工作。

其实这个解法有一个小瑕疵——前 \(i-1\) 天工作的 \(L[i-1]\) 天中,可能有某一天与第 \(i\) 天的距离小于等于 \(C\),按理说这样第 \(i\) 天便不能参与工作,此题无解。但显然这道题默认是有解的,恰好规避了这种情况,减少了我们的讨论。

细节移步代码。

#include <iostream>
#include <cstring>
#include <cstdio>

#define Maxn 200010

using namespace std;

inline int read() {
    int x = 0, f = 1;
    char c = getchar();
    while(c < ‘0‘ || c > ‘9‘) {
        if(c == ‘-‘) f = -1;
        c = getchar();
    }
    while(‘0‘ <= c && c <= ‘9‘) {
        x = x * 10 + c - ‘0‘;
        c = getchar();
    }
    return x * f;
}

int n, k, c, l[Maxn], r[Maxn];
char ch[Maxn];

int main() {
    n = read(); k = read(); c = read();
    scanf(" %s", ch + 1);
    int j = 0;
    for(int i = 1; i <= n; ++i) {
        if(ch[i] == ‘o‘ && i > j) {
            l[i] = l[i - 1] + 1;
            j = i + c;
        }
        else l[i] = l[i - 1];
    }
    j = n + 1;
    for(int i = n; i; --i) {
        if(ch[i] == ‘o‘ && i < j) {
            r[i] = r[i + 1] + 1;
            j = i - c;
        }
        else r[i] = r[i + 1];
    }
    for(int i = 1; i <= n; ++i) {
        if(ch[i] == ‘o‘ && l[i - 1] + r[i + 1] + 1 == k) printf("%d\n", i);
    }
    return 0;
}

F Division or Substraction

一道比较基础的数论题目。

看到数据范围,首先显然想到约数枚举的 \(\sqrt{n}\)

第一种情况,\(k\) 只能通过相减的方式使得 \(n\) 降至 \(1\) —— 只需判断 \(k\) 是否整除 \(n-1\) 即可。注意只用枚举 \(k \leq \sqrt{n}\) 的部分,另一部分对称可以得到。

第二种情况,\(k\) 可以通过相除或相减的方式使得 \(n\) 降至 \(1\),这个时候就先让 \(k\) 尽量地整除 \(n\),将整除后剩余的数字按照第一种方式判断即可。注意此时仍然只需要枚举 \(k \leq \sqrt{n}\) 的部分,因为对于 \(k > \sqrt{n}\) ,第一次整除后一定有 \(n‘<k\),最后一定无法只剩下 \(1\)

还有一点特判,细节参见代码。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>

#define LL long long
#define rg register

using namespace std;

inline LL read() {
    LL x = 0, f = 1;
    char c = getchar();
    while(c < ‘0‘ || c > ‘9‘) {
        if(c == ‘-‘) f = -1;
        c = getchar();
    }
    while(‘0‘ <= c && c <= ‘9‘) {
        x = x * 10 + c - ‘0‘;
        c = getchar();
    }
    return x * f;
}

LL n, m, ans1, ans2;

int main() {
    n = read(); m = sqrt(n);
    if(n == 1) {cout << 1 << endl; return 0;}
    if(n == 2) {cout << 1 << endl; return 0;}
    for(rg LL i = 2; i <= m; ++i) {
        LL now = n;
        if(!(now % i)) {
            while(!(now % i)) now /= i;
            if(!((now - 1) % i)) ++ans1;
        }
        else if(!((now - 1) % i)) {
            if(i * i == now - 1) ++ans2;
            else ans2 += 2;
        }
    }
    printf("%lld\n", ans1 + ans2 + 2);
    return 0;
}

Atcoder ABC161 小结

原文:https://www.cnblogs.com/sqrtyz/p/13418700.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!