第八届“图灵杯”NEUQ-ACM程序设计竞赛题解报告 [A题待补]

比赛传送门

A B C D E F G H I J K L

√ 表示已完成,○表示待补

B 小宝的幸运数组

题意

定义一个和能被幸运数字整除的数组被称为“幸运数组”
现给幸运数字 k k k 和长度为 n n n 的数组 a a a,求最长的幸运子数组。

思路

首先求出数组 a a a 的前缀和数组,然后对前缀和数组的各位对 k k k 取模运算。先记录各种模数最先出现的位置,再找最后出现的相同模数的位置并维护幸运子数组长度的最大值。

Accepted code

/*
 * @Autor: CofDoria
 * @Date: 2021-01-30 14:51:53
 * @LastEditTime: 2021-01-31 13:01:01
 */
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
int t;
long long n, k;
long long a[100005];
long long pre[100005];
long long ans;

int main() {
    
    
    a[0] = 0;
    scanf("%d", &t);
    while (t--) {
    
    
        ans = -1;
        scanf("%lld%lld", &n, &k);
        memset(pre, -1, sizeof(pre));
        pre[0] = 0;
        for (long long i = 1; i <= n; i++) {
    
    
            scanf("%lld", &a[i]);
            a[i] += a[i - 1];
            long long idx = a[i] % k;
            if (pre[idx] == -1)
                pre[idx] = i;
            else
                ans = max(ans, i - pre[idx]);
        }
        printf("%lld\n", ans);
    }
}

在这里插入图片描述

C 上进的凡凡

题意

求数组中非递减子数组的个数。

思路

遍历数组,每当当前元素不小于前一元素,给定数组的非递减子数组的个数都会增加,增加的个数和以当前元素为结尾的最长非递减子数组的长度的相同。

Accepted code

#include <cstdio>
using namespace std;

long long n;
long long ans = 0;
long long a, la = -1;

int main() {
    
    
    scanf("%lld", &n);
    for (long long i = 0, cnt = 0; i < n; i++) {
    
    
        scanf("%lld", &a);
        if (a >= la)
            ++cnt;
        else
            cnt = 1;
        ans += cnt;
        la = a;
    }
    printf("%lld", ans);
}

在这里插入图片描述

D Seek the Joker I

题意

有一堆 n n n 张牌构成的牌堆,每人最多抽 k k k 张牌,最少抽 1 1 1 张,该牌堆的最后一张作为乌龟,抽中的人输掉比赛,假如两位玩家绝顶聪明,先手者输则输出ma la se mi no.1!,否则输出yo xi no forever!

思路

巴什博奕

若牌堆共有 ( k + 1 ) ∗ x + 1 (k + 1)*x+1 (k+1)x+1 张牌(x为任意非负整数) ,后手玩家都可以根据先手玩家的操作取牌,使两人一回合拿牌数和为 ( k + 1 ) (k+1) (k+1),保证除乌龟的最后一张牌为自己取得。该情况下后手必胜。
否则,先手玩家都可以保证除乌龟的最后一张牌为自己取得。该情况下先手必胜。

Accepted code

#include <cstdio>
using namespace std;
int t;
long long n, k;

int main() {
    
    
    scanf("%d", &t);
    while (t--) {
    
    
        scanf("%lld%lld", &n, &k);
        if ((n - 1) % (k + 1) == 0)
            printf("ma la se mi no.1!\n");
        else
            printf("yo xi no forever!\n");
    }
}

在这里插入图片描述

E Seek the Joker II

题意

有一由 n n n 张牌组成的牌堆,第 x x x 张为乌龟,取到的人则输掉比赛。
两个人轮流取牌,每次可以从上方或下方取任意张牌,或从上方和下方取相同张数的牌。
假如两位玩家绝顶聪明,先手者输则输出ma la se mi no.1!,否则输出yo xi no forever!

思路

威佐夫博弈

威佐夫博弈公式:
两堆石子为 ( x , y ) ( x < y ) (x,y)(x<y) (x,y)(x<y),当且仅当 f l o o r ( ( y − x ) ∗ ( 5 + 1 ) 2 ) = x floor((y-x)*\frac{(\sqrt{5}+1)}{2})=x floor((yx)2(5 +1))=x 时,先手必败,否则先手必胜。

对该题来说,两堆石子就是上下两部分牌堆 ( n − x , x − 1 ) (n-x,x-1) (nx,x1),代入公式即可。

Accepted code

/*
 * @Autor: CofDoria
 * @Date: 2021-01-31 11:09:14
 * @LastEditTime: 2021-01-31 12:21:37
 */
#include <algorithm>
#include <cmath>
#include <cstdio>
using namespace std;
int t;
long long n, x;

int main() {
    
    
    scanf("%d", &t);
    while (t--) {
    
    
        scanf("%lld%lld", &n, &x);
        long long a = n - x, b = x - 1;
        if (a < b) swap(a, b);
        if (floor((a - b) * (pow(5.0, 0.5) + 1) / 2) == b)
            puts("ma la se mi no.1!");
        else
            puts("yo xi no forever!");
    }
    return 0;
}

在这里插入图片描述

F 成绩查询ing

题意

依次输入学生的名字、成绩、性别 ( 1 / 2 ) (1/2) (1/2)和学号。
然后输入查询操作 操作 1 1 1 为输入名字查询学生、成绩、性别和学号;操作 2 2 2 为输入成绩按字典序输出学生名字(无该成绩的学生则不输出)。

思路

用结构体储存学生信息,然后重载 < < < 符号,按名字的字典序比较结构体的大小。
在输入结束后 s o r t sort sort 一遍。
而后对于操作 1 1 1,用 l o w e r _ b o u n d lower\_bound lower_bound 函数寻找该名字的学生。对于操作 2 2 2,按顺序寻找该成绩学生,输出结果即是字典序排序。

Accepted code

/*
 * @Autor: CofDoria
 * @Date: 2021-01-30 12:51:38
 * @LastEditTime: 2021-02-04 15:54:15
 */
#include <bits/stdc++.h>
using namespace std;

#define db double
#define ll long long
#define inf 0x3f3f3f3f
#define s(a, n) memset(a, n, sizeof(a))
#define debug(a) cout << '#' << a << '#' << endl
#define rep(l, a, b) for (register ll l = a; l < b; ++l)
#define per(l, a, b) for (register ll l = a; l >= b; --l)
#define _ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define _forit(i, c) \
    for (__typeof__((c).begin()) i = (c).begin(); i != (c).end(); ++i)

bool fi = true;
const unsigned long long MOD = 1e9 + 7;

inline ll gcd(ll a, ll b) {
    
     return (b == 0 ? a : gcd(b, a % b)); }

int n, m, op;

struct stu {
    
    
    int gender, grade, no;
    string name;
    stu(int a = 0, int b = 0, int c = 0, string s = "")
        : gender(a), grade(b), no(c), name(s) {
    
    }
    bool operator<(const stu x) const {
    
     return name < x.name; }
} s[100005];

inline bool cmp(stu a, stu b) {
    
     return a.grade < b.grade; }

int main() {
    
    
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
    
    
        cin >> s[i].name;
        scanf("%d%d%d", &s[i].grade, &s[i].gender, &s[i].no);
    }
    sort(s, s + n);
    scanf("%d", &m);
    for (int i = 0; i < m; i++) {
    
    
        scanf("%d", &op);
        if (op == 1) {
    
    
            string temp;
            cin >> temp;
            stu *p = lower_bound(s, s + n, stu(0, 0, 0, temp));
            if (p->name == temp)
                printf("%d %d %d\n", p->grade, p->no, p->gender);
        } else if (op == 2) {
    
    
            int score;
            scanf("%d", &score);
            for (int j = 0; j < n; j++)
                if (s[j].grade == score) printf("%s\n", s[j].name.c_str());
        }
    }
    // fclose(stdin);
    // fclose(stdout);
    return 0;
}

在这里插入图片描述

G 贪吃的派蒙

题意

k k k 份点心, n n n 个人排成一个队列轮流吃,队首的人吃完则会进入回到队尾,等待下一轮。
每个人有每次都有一个吃点心的上限 a i a_i ai,即轮到第 i i i 个人吃的时候,每轮最多吃 a i a_i ai个,最少吃 1 1 1 个。
在这个队列中有一个特殊的人,她的上限是所有人中最高的,而且她每次都会尽可能吃最多的点心,队列中的其他人想要让某次轮到她的时候刚好吃完 k k k 份点心。问是否可行,可行输出YES,否则输出NO

思路

寻找到数组 a a a 中的最大值,记录在最大值左边的最大、最小和与右边的最大、最小和,计算从第一次轮到这个人开始,寻找是否存在以下情况:

  • 所有人尽可能少进食点心,总共消耗的点心数小于等于 k k k
  • 所有人尽可能多进食点心,总共消耗的点心数大于等于 k k k

当存在以上情况,则说明存在刚好轮到此人进食时, k k k 份点心刚好全部消灭

Accepted code

/*
 * @Autor: CofDoria
 * @Date: 2021-01-30 12:51:38
 * @LastEditTime: 2021-02-08 15:56:46
 */
#include <bits/stdc++.h>
using namespace std;

#define db double
#define ll long long
#define inf 0x3f3f3f3f
#define s(a, n) memset(a, n, sizeof(a))
#define debug(a) cout << '#' << a << '#' << endl
#define rep(l, a, b) for (ll l = a; l < b; ++l)
#define per(l, a, b) for (ll l = a; l >= b; --l)
#define _ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define _forit(i, c) \
    for (__typeof__((c).begin()) i = (c).begin(); i != (c).end(); ++i)

bool fi = true;
const unsigned long long MOD = 1e9 + 7;

inline ll gcd(ll a, ll b) {
    
     return (b == 0 ? a : gcd(b, a % b)); }

ll t;
ll n, k;
ll a[100005];
ll maxn;
ll a1, a2;
ll a1num;

int main() {
    
    
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    scanf("%lld", &t);
    while (t--) {
    
    
        maxn = 0;
        scanf("%lld%lld", &n, &k);
        rep(i, 0, n) {
    
    
            scanf("%lld", &a[i]);
            if (a[i] > maxn) {
    
    
                maxn = a[i];
                a1num = i;
            }
        }
        bool flag = false;
        a1 = a2 = 0;
        rep(i, 0, n) {
    
    
            if (!flag) {
    
    
                if (a[i] == maxn) {
    
    
                    flag = true;
                } else
                    a1 += a[i];
            } else
                a2 += a[i];
        }
        ll round = 0;
        flag = false;
        while (1) {
    
    
            if (a1num + round * (maxn + n - 1) > k) break;
            if (a1num + round * (maxn + n - 1) <= k &&
                a1 + round * (a1 + maxn + a2) >= k) {
    
    
                flag = true;
                printf("YES\n");
                break;
            }
            ++round;
        }
        if (!flag) printf("NO\n");
    }
    // fclose(stdin);
    // fclose(stdout);
    return 0;
}

在这里插入图片描述

H 数羊

题意

给定两个数 n n n m m m,并按以下方法计算并输出结果
在这里插入图片描述

思路

打表找规律,可以发现当 m = 0 m=0 m=0 时结果等于 n + 2 n+2 n+2,( n n n 1 1 1 时除外); m = 1 m=1 m=1 时,结果为 2 ∗ n 2*n 2n m = 2 m=2 m=2 时,结果为 2 n 2^n 2n

Accepted code

/*
 * @Autor: CofDoria
 * @Date: 2021-01-30 16:01:23
 * @LastEditTime: 2021-01-31 20:28:19
 */
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;

int t;
long long n, m;
long long MOD = 998244353;

// long long A(long long n, long long m) {
    
    
//     if (n == 1 && m == 0) return 2;
//     if (n == 0) return 1;
//     if (m == 0) return (n + 2) % MOD;
//     return A(A(n - 1, m), m - 1) % MOD;
// }

long long f(long long n) {
    
    
    long long ans = 1;
    long long t = n;
    while (t) {
    
    
        ans *= 2;
        ans -= (ans > MOD ? MOD : 0);
        --t;
    }
    return ans;
}

int main() {
    
    
    // freopen("out.txt", "w", stdout);
    // for (long long i = 1; i <= 1000; i++) {
    
    
    //     for (long long j = 0; j <= 2; j++) {
    
    
    //         printf("#A(%lld,%lld):%lld# ", i, j, A(i, j));
    //         if (j == 2) puts("");
    //     }
    // }
    while (~scanf("%d", &t)) {
    
    
        while (t--) {
    
    
            scanf("%lld%lld", &n, &m);
            if (m == 0) {
    
    
                if (n == 1)
                    puts("2");
                else
                    printf("%lld\n", (n + 2) % MOD);
            } else if (m == 1) {
    
    
                printf("%lld\n", (2 * n) % MOD);
            } else {
    
    
                printf("%lld\n", f(n));
            }
        }
    }
    // fclose(stdout);
    return 0;
}

在这里插入图片描述

I 买花

题意

需要买 n n n 朵花,花要分 k k k 天买( k > 1 k>1 k>1),第一天可以买任意朵,之后每一天都要买前一天的 2 2 2 倍,最多不超过 15 15 15 天,若刚好买到 n n n 朵花,输出YE5,否则输出N0

思路

通过打表发现,只要 n n n 3 , 7 , 15 , 31 … … 3, 7, 15, 31…… 3,7,15,31的倍数(该数列是第i-1天的总购花数数列,以第一天买一朵花为基础),就可以刚好买到 n n n 朵花。注意输出不是YESNO

Accepted code

#include <cstdio>
using namespace std;
int t;
long long n;

int a[] = {
    
    1,   3,    7,    15,   31,   63,    127,  255,
           511, 1023, 2047, 4095, 8191, 16383, 32767};

int main() {
    
    
    // printf("int a[]={");
    // bool fi=true;
    // for(int i=0,l=1;i<15;i++){
    
    
    //    if(fi)fi=!fi;
    //    else printf(",");
    //    printf("%d",l);
    //    l*=2;
    //    ++l;
    // }
    // printf("};");
    scanf("%d", &t);
    while (t--) {
    
    
        scanf("%lld", &n);
        bool ok = false;
        for (int i = 1; i < 15; i++) {
    
    
            if (n % a[i] == 0) {
    
    
                ok = true;
                break;
            }
        }
        if (ok)
            printf("YE5\n");
        else
            printf("N0\n");
    }
}

在这里插入图片描述

J 这是一题简单的模拟

题意

给出一张图和 k k k 条路线,问 k k k 条路线中对所有点遍历有且仅有一次的路线是否存在(其中0默认为起点和终点),并输出路径长度最小的总权值。

思路

按题意模拟即可
与2020年天梯赛 L2-4 网红点打卡攻略题完全一致,仅范围和输出内容小有更改。

Accepted code

/*
 * @Autor: CofDoria
 * @Date: 2020-11-28 13:31:52
 * @LastEditTime: 2021-01-30 23:15:33
 */
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <stack>
using namespace std;

int a[305][305];
int vis[305];
int n, m;
int k;
int ans[305];
// bool ok;
int minc = (int)(1e9 + 7);
int cost;
int cnt = 0;
int ansn;
int i;

void dfs(int idx) {
    
    
    if (idx + 1 == n) {
    
    
        if (a[ans[idx]][0]) {
    
    
            cnt++;
            cost += a[ans[idx]][0];
            if (minc > cost) {
    
    
                ansn = i;
                minc = cost;
            }
            return;
        }
        return;
    }
    if (vis[ans[idx + 1]] || !a[ans[idx]][ans[idx + 1]]) {
    
    
        // ok = false;
        return;
    }
    cost += a[ans[idx]][ans[idx + 1]];
    vis[ans[idx]] = 1;
    dfs(idx + 1);
}

int main() {
    
    
    scanf("%d%d", &n, &m);
    memset(a, 0, sizeof(a));
    for (int iii = 0; iii < m; iii++) {
    
    
        int s, t, v;
        scanf("%d%d%d", &s, &t, &v);
        if (!a[s][t] || (a[s][t] > v)) {
    
    
            a[s][t] = v;
            a[t][s] = v;
        }
    }
    scanf("%d", &k);
    for (i = 1; i <= k; i++) {
    
    
        int nn;
        scanf("%d", &nn);
        for (int ii = 0; ii < nn; ii++) {
    
    
            scanf("%d", &ans[ii]);
        }
        if (nn == n && a[0][ans[0]]) {
    
    
            memset(vis, 0, sizeof(vis));
            // ok = true;
            cost = a[0][ans[0]];
            dfs(0);
        }
    }
    // printf("%d\n%d %d", cnt, ansn, minc);
    printf("%d", minc == (int)(1e9 + 7) ? -1 : minc);
    return 0;
}

在这里插入图片描述

K 黑洞密码

题意

给出一段字符串

1.确定讯息的长度为 32 32 32

2.字符串中第 4 n + 1 ∼ 4 n + 4 4n+1\sim4n+4 4n+14n+4的字母和第 4 n + 4 4n+4 4n+4的字母和第 4 n + 1 ∼ 4 n + 4 4n+1\sim4n+4 4n+14n+4 ( 0 ≤ n ≤ 3 ) (0 \leq n \leq 3) (0n3)的数字为一组,共 4 4 4 组;

3.每组的第1,2,3,4个字符分别往后推每组第1,2,3,4个数字个数 例:如第一个字母为a,第一个数字为3,转换后变为d,‘z’之后是’B’,‘Z’之后是’b’;

4.将每组内部字母的顺序颠倒;

5.将四组字符合并就是最后的讯息。

思路

按题意模拟即可

Accepted code

/*
 * @Autor: CofDoria
 * @Date: 2021-01-31 11:09:14
 * @LastEditTime: 2021-02-01 12:01:18
 */
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
using namespace std;
int zcnt = 0, scnt = 0;
char a[16];
int b[16];
char str[45];
string s[4];

int main() {
    
    
    scanf("%s", &str);
    for (int i = 0, sz = strlen(str); i < sz; i++) {
    
    
        if (isalpha(str[i])) {
    
    
            if (zcnt < 16) {
    
    
                a[zcnt] = str[i];
                ++zcnt;
            }
        }
        if (isdigit(str[i])) {
    
    
            if (scnt < 16) {
    
    
                b[scnt] = str[i] - '0';
                ++scnt;
            }
        }
        if (scnt >= 16 && zcnt >= 16) break;
    }
    for (int i = 0; i < 4; i++) s[i].resize(4);
    for (int i = 0; i < 16; i++) {
    
    
        while (b[i]) {
    
    
            if (a[i] == 'Z') {
    
    
                a[i] = 'b';
            } else if (a[i] == 'z') {
    
    
                a[i] = 'B';
            } else
                ++a[i];
            --b[i];
        }
        s[i / 4][i % 4] = a[i];
    }
    for (int i = 0; i < 4; i++)
        reverse(s[i].begin(), s[i].end()), printf("%s", s[i].c_str());
}

在这里插入图片描述

L 建立火车站

题意

n n n 个坐标,在其中加入 k k k 个坐标,使两两坐标之间的差尽可能小。

思路

二分寻找符合条件的最小差。

Accepted code

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
int t;
long long n, k;
long long a[100005];

long long _ceil(long long a, long long b) {
    
    
    long long temp = (long long)(a / b);
    if (temp * b == a) return temp;
    return temp + 1;
}

bool f(long long dist) {
    
    
    long long lv = k;
    for (int i = 1; i < n; i++) {
    
    
        if (a[i] - a[i - 1] > dist) {
    
    
            long long num = _ceil((a[i] - a[i - 1]), dist) - 1;
            // printf("%lld %lld\n",_ceil((a[i]-a[i-1]),dist),dist);
            if (lv - num >= 0) {
    
    
                lv -= num;
            } else
                return false;
        }
    }
    return true;
}

int main() {
    
    
    long long l = 0, r = 1, mid;
    scanf("%lld%lld", &n, &k);
    for (int i = 0; i < n; i++) {
    
    
        scanf("%lld", &a[i]);
    }
    sort(a, a + n);
    for (int i = 1; i < n; i++) r = max(r, a[i] - a[i - 1]);
    mid = (l + r) >> 1;
    while (r - l > 1) {
    
    
        // printf("%lld %lld %lld\n",l,mid,r);
        mid = (l + r) >> 1;
        if (f(mid))
            r = mid;
        else
            l = mid;
    }
    printf("%lld", r);
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_46144509/article/details/113467534