Atcoder Beginner 106 ~ 108 C题题解

本人因为身体原因博客断更了很长时间,唉,程序员还是要注重身体,尤其是我这种辣鸡的程序员,如果代码能力不高然后再身体不好那可就gg了啊T^T

话说ABC107是我打Atcoder开始以来,排名最靠前的,第145,竟然排名页第8页就能看到我~这是从没有过的hhh,不过也就涨了70多分 >^<

好了闲话不多说了~直接上这三场比赛的C题题解:

< ABC106 - C >

题意:

就是,先给定一个原串,然后每天经过一次骚变换,持续 5 * 10 ^ 15天。关于这个骚变换,举个栗子~

比如原串是,1214

一天后变成 12214444

再过一天又变成 1222214444444444444444,以此类推。。。

就是说给定一个串,串中某一位置的数字是几,下一天他就变成几个这个数,1214之所以在一天后变成12214444,就是第二个位置的2变成了2个2,第四个位置的4变成4个4,以此类推,不再赘述了。

然后给你一个 k(long long 以内)值,是表示位置,问你,经过5 * 10 ^ 15天这种变换,现在这个串第k个位置的数字是多少。

思路:

我是这么想的,因为 5 * 10 ^ 15天,真的太大了,所以枚举这种蠢事是不存在的了,那我就看一下这个串的增长规律,除了1每次个数不动以外,其余数字 t 在下一天都会变成 t 个 t,所以说,比如我开头不是1,假设是除1以外,最小的2吧,那每经过一天,我的2都会呈指数增长,经过5 * 10 ^ 15天后,如果我开头是最小的2的话,那我首先就有 2 的 (5 * 10^15)次幂个2了,那更大的数就更不用说了,远超long long 的范围了,所以说,只要开头没碰到1,那我答案一定就是开头的数了,再深入想想,如果开头就是多个1连着呢,那我不管经过多少天变换,这开头这一堆连续的1都不动,那我的k值(要求的位置),如果比这一堆连续的1的个数少,那就打印1,否则就打印第一个不是1的数即可~

本人AC代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <map>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
char s[107];
ll k;
int ans;

int main() {
    scanf("%s %lld", s, &k);
    int l = strlen(s);
    bool flg = 0;
    int cnt = 0;
    for(int i = 0; i < l; i++) {
        if(s[i] == '1') cnt++;
        else {
            flg = 1;
            ans = s[i] - '0';
            break;
        }
    }
    if(!flg) puts("1");
    else {
        if(k > cnt) cout << ans << endl;
        else cout << 1 << endl;
    }
}

< ABC107 - C >

题意:

你有n个蜡烛,摆在数轴上的n个坐标上,你从坐标原点0出发,一共需要点燃k个蜡烛,问最少走多少步~当然了,一步只能走1个单位哦~

思路:

我的思路就~比较简单吧~虽然是贪心,但我就找了个规律,其实也就是个枚举,

我进行了以下两步操作,就是核心代码:

if(i + k - 1 >= 1 && i + k - 1 <= n) t1 = abs(a[i]) + abs(a[i + k - 1] - a[i]);

if(i - k + 1 >= 1 && i - k + 1 <= n) t2 = abs(a[i]) + abs(a[i - k + 1] - a[i]);

就是分别先向左或向右走,然后看下一步是顺着这个方向走,还是逆着走更贪心,听我的,自己画一下数轴。模拟一下怎么走,就知道了,

核心代码:

for(int i = 1; i <= n; i++) {
        int t1 = Inf, t2 = Inf, tmp;
        if(i + k - 1 >= 1 && i + k - 1 <= n) t1 = abs(a[i]) + abs(a[i + k - 1] - a[i]);
        if(i - k + 1 >= 1 && i - k + 1 <= n) t2 = abs(a[i]) + abs(a[i - k + 1] - a[i]);
        tmp = min(t1, t2);
        minT = min(minT, tmp);
    }

用最小值minT维护一下就ok了~一定要画个数轴自己模拟一下这个过程,真的一目了然~

本人AC代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <queue>
#include <map>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxx = 1e5 + 7;
const int Inf = 1 << 30;
int n, k;
int a[maxx];

int main() {
    int minT = Inf;
    cin >> n >> k;
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i <= n; i++) {
        int t1 = Inf, t2 = Inf, tmp;
        if(i + k - 1 >= 1 && i + k - 1 <= n) t1 = abs(a[i]) + abs(a[i + k - 1] - a[i]);
        if(i - k + 1 >= 1 && i - k + 1 <= n) t2 = abs(a[i]) + abs(a[i - k + 1] - a[i]);
        tmp = min(t1, t2);
        minT = min(minT, tmp);
    }
    cout << minT << endl;
}

我顺便说一下B题吧,我看貌似500多人都没过,题意就是,给个二维字符矩阵,删掉全是点的所有行所有列,打印新矩阵。

我先标记全是点的行,然后标记全是点的列,再把标记过的所有点的列,里面的点,都换成感叹号。(这种看似脱裤子XX的操作就是为了方便我打印),没被标记的行,我就按顺序输出,碰到!,我就知道这个点是处于一个全是点的列中,我就不打印它就行了。下面是我AC代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <queue>
#include <map>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
int n, m;
char s[107][107];
bool vis[107];

int main() {
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= n; i++) scanf("%s", s[i]);
    for(int i = 1; i <= n; i++) {
        bool flag = 1;
        for(int j = 0; j < m; j++) {
            if(s[i][j] != '.') {
                flag = 0;
                break;
            }
        }
        if(flag) vis[i] = 1;
    }
    for(int j = 0; j < m; j++) {
        bool flag = 1;
        for(int i = 1; i <= n; i++) {
            if(s[i][j] != '.') {
                flag = 0;
                break;
            }
        }
        if(flag) {
            for(int i = 1; i <= n; i++) s[i][j] = '!';
        }
    }
    for(int i = 1; i <= n; i++) {
        if(!vis[i]) {
            for(int j = 0; j < m; j++) {
                if(s[i][j] != '!') printf("%c", s[i][j]);
                if(j == m - 1) printf("\n");
            }
        }
    }
}

< ABC108 - C >

题意:

给两个数n和k,在1~n中选三个数,组成一个三元有序对 <a,b,c>,使得 a+b, b+c, a+c 都是k的倍数,求有序对个数。

思路:

这题代码短的一匹,就是有些不好想,首先,给定的 n,能提供出来的 k 的倍数的个数,是 n / k 个。

那么我这些有序对的基数,就是 (n / k) ^ 3个,这就是直接构成的有序对个数。那么还有一些间接的,如果我 k 是偶数,那么我先将 k 除2,得到一个数,那么两个 k / 2的奇数倍的和,一定是 k 的倍数,证明一下:

有任意两奇数a,b,

a * (k / 2) + b * (k / 2) = (a + b) * (k / 2),

由于两奇数和是偶数,那我将上式2约掉,得到  [(a+b) / 2] * k,显然是k的倍数~

那这种 k / 2 的奇数倍数的个数就是,k / 2的倍数减掉 k / 2的偶数倍(也就是k的倍数的个数),即 tmp = n / (k / 2) - n / k

那我间接形成的有序对个数就是 tmp ^ 3 了,所以当k是偶数的情况下,在原来那个基数上加上tmp的立方即可~

本人AC代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <queue>
#include <map>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
int n, k;
ll ans;

ll trip(ll a) { return 1ll * a * a * a; }

int main() {
    cin >> n >> k;
    ll ans1 = n / k;
    ll ans2 = 0;
    if(!(k & 1)) ans2 = n / (k / 2) - n / k;
    ans = trip(ans1) + trip(ans2);
    cout << ans << endl;
}

追加一个题~今天做的ICPC南京网络预赛的签到题,本场比赛我唯一会做的题 T^T

题意:

给你一个数n (long long 以内),然后规定 f(n) = 1 * 1! + 2 * 2! + 3 * 3! + ... ... + (n - 1) * (n - 1)!

求 f(n) % n.

思路:

这题,我推了半天,果不其然,答案就是,n - 1,注意long long 哦~

我的AC代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
int cas;
ll n;

int main() {
    scanf("%d", &cas);
    while(cas--) {
        scanf("%lld", &n);
        printf("%lld\n", n - 1);
    }
}

猜你喜欢

转载自blog.csdn.net/EricGipsy/article/details/82291452