第八届“图灵杯”NEUQ-ACM程序设计竞赛个人赛(同步赛)

传送门

B-小宝的幸运数组

题目描述

对于小宝来说,如果一个数组的总和能够整除他的幸运数字k,就是他的幸运数组,而其他数组小宝都很讨厌。现在有一个长度为n的数组,小宝想知道这个数组的子数组中,最长的幸运子数组有多长。

对于子数组的定义,如果可以通过从开头和从结束分别删除若干个(可以为零或全部,前后删除个数不必相同)元素来从数组b获得数组a,则称数组a是数组b的子数组。(子数组包含原数组,但不包含空串)

题意:

对一个数组求数组和能整除k的子数组的个数,子数组是指原数组中任意连续的数组,长度大于0即可。

思路:

暴力解法是两重for循环对前缀和求差,找最长的。但会TLE。所以进行一下改进:观察题意要的是能整除k的子数组,所以可以让前缀和对k取模,这样如果模相等,则求差的时候得到的中间的数组和就可以整除k,只需要找到最大的长度即可。方法是利用两个数字分别存每个取模以后的最大和最小的位置,最后进行一次循环输出最大减最小的差即可。

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
typedef long long ll;
ll tr[100005], ma[100005], mi[100005], have[100005];
ll x, t, n, k, a;
int main()
{
    cin>>t;
    while(t--)
    {
        a = 0;//a用来判断最终结果是否输出-1
        memset(tr, 0, sizeof(tr));//初始化
        memset(ma, 0, sizeof(ma));//ma数组用来存每一个余数出现的最大位置,因为是求max,则需让数组初始化为-1e9,但本题的值都大于等于0,所以没必要赋-1e9
        memset(have, 0, sizeof(have));//have数组用来存每一个余数出现的次数,只有出现次数大于1,其相减才能得到能整除k的子数组
        for(int i = 0; i < 100005; i++)
            mi[i] = inf;//mi数组用来存每一个余数出现的最小位置,因为是求min,所以赋初始值为最大的数
        mi[0] = 0;
        /*这个和下面的那个初始化很重要,因为可能是前缀和本身就能整除k,此时余数就是0,而这个时候不需要两个的差,所以要给have[0] = 1,这样0这个位置就有资格去求了,而且mi[0] = 0,不会出现比0还小的位置,减的时候就正确*/
        have[0] = 1;
        cin>>n>>k;
        for(int i = 1; i <= n; i++){
            cin>>x;
            tr[i] = tr[i - 1] + x;//求前缀和
        }
        for(ll i = 1; i <= n; i++){
            tr[i] = tr[i] % k;//对前缀和取模
            mi[tr[i]] = min(mi[tr[i]], i);//存余数出现的最小位置
            ma[tr[i]] = max(ma[tr[i]], i);//存余数出现的最大位置
            have[tr[i]]++;
            if(have[tr[i]] >= 2)
                a = 1;
        }
        if(a == 0)
            cout<<-1<<endl;
        else{
            ll ans = 0;
            for(ll i = 0; i < 100005; i++){
                if(have[i] >= 2)
                    ans = max(ans, ma[i] - mi[i]);
            }
            cout<<ans<<endl;
        }
    }
    return 0;
}

C-上进的凡凡

题目描述

凡凡是一个上进的人,他的人生没有下坡路,他也讨厌带有”下坡路“的东西。

所以,对于凡凡来说,只有非降序的数组才是nice的(如:1,2,2,3,4,5,5);若数组元素个数为1,也满足非降序,也是nice的。

现在有一个长度为n的数组,凡凡想知道它的子数组中有多少个数组是nice的。

题意:

让你求非降序的子数组的个数,相等也算非降序。

思路:

先举个栗子:1,2,3,4,3,1,2从1开始看,子数组有[1];再看2,它和1构成非降序,所以子数组又多了[1,2]和[2];再看3,它和1,2构成非降序,所以子数组又多了[1,2,3]和[2,3]和[3];再看4,它和1,2,3构成非降序,所以字数组又多了[1,2,3,4]和[2,3,4]和[3,4]和[4];再看第二个3,他和前面的不构成非降序,所以子数组又多了个[3];再看第2个1,他和前面的也不构成非降序,所以子数组又多了[1];再看第2个2,他和前面的构成降序,所以子数组又多了[1,2]和[2];所以子数组的数量是1 + 2 + 3 + 4 + 1 + 1 + 2。规律就是每多一个非降序的数,则加的值是上次加的值加一,而每多一个降序的数,就加1。还有一种做法是将数组分成若干个非降序的子数组,然后利用(n + 1) * n / 2的公式带入所有的子数组的长度然后相加,如果是降序的就1个1个的分开。这个公式其实也就是我上面的那个规律的等差求和公式而已,不过我个人感觉比较麻烦,你每次都得找两个端点才能套公式,而我的那个只需要一次模拟,时时更新值即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, tr[100005];
int main()
{
    cin>>n;
    for(int i = 1; i <= n; i++)
        cin>>tr[i];
    tr[0] = 1e9;
    ll sum = 0, k = 1;
    for(int i = 1; i <= n; i++)
    {
        if(tr[i] >= tr[i - 1])
        {
            k++;
        }
        else
            k = 1;
        sum += k;
    }
    cout<<sum<<endl;
    return 0;
}

Seek the Joker I

题目描述

长达数日的春日祭终于告一段落,作为巫女的朝野芳乃在打扫完神社本决定好好享受一下久违的宁静。然而守护了神刀数百年的丛雨难耐寂寞,希望芳乃能陪她一起玩扑克消解愁闷。

芳乃并不擅长市井的游戏,所以总是输多赢少。而昨日被芳乃的神乐舞深深吸引,以致一早就前来建实神社希望能再睹芳华的你碰巧听见了此事。尽管不知道为什么美丽的巫女要自言自语地为玩扑克而苦恼,但你毅然决然地毛遂自荐,希望能为芳乃一解眉间愁。

芳乃告诉了你丛雨准备了n张扑克牌作为牌堆,每人每次至多从牌堆顶部抽k张牌,至少抽1张牌。牌堆底部的最后一张牌作为乌龟,抽中的将输掉这一轮比赛。芳乃想知道在你的帮助下,她和丛雨都采取积极策略时,她自己是否一定能获胜。作为被丛雨邀请的一方,每轮游戏都是芳乃先抽。

因为看不见丛雨而误认芳乃罹患精神分裂的你在不由感叹红颜薄命的同时,决定尽全力帮助芳乃完成她的委托。

声明:本题中的所有角色在剧情发生时均已超过18岁。

题意:

芳乃和从雨玩游戏。n张牌,每人最多每次最多取k张牌,至少取一张,抽到最后一张牌的人输,芳乃先抽。

芳乃赢输出“yo xi no forever!”否则输出”ma la se mi no.1!“

思路:

简单的巴什博弈,之前是n张牌,取完最后一张的赢,本题只需让n-1即可得到裸的巴什博弈。判(n - 1)%(k + 1),如果等于0,先手输,否则先手必胜!

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n, k, t;
    cin>>t;
    while(t--){
        cin>>n>>k;
        if((n - 1) % (k + 1) == 0)
            cout<<"ma la se mi no.1!\n";
        else
            cout<<"yo xi no forever!\n";
    }
    return 0;
}

E-Seek the Joker II

题目描述

长达数日的春日祭终于告一段落,作为巫女的朝野芳乃在打扫完神社本决定好好享受一下久违的宁静。然而守护了神刀数百年的丛雨难耐寂寞,希望芳乃能陪她一起玩扑克消解愁闷。

芳乃并不擅长市井的游戏,所以总是输多赢少。而昨日被芳乃的神乐舞深深吸引,以致一早就前来建实神社希望能再睹芳华的你碰巧听见了此事。尽管不知道为什么美丽的巫女要自言自语地为玩扑克而苦恼,但你毅然决然地毛遂自荐,希望能为芳乃一解眉间愁。

芳乃告诉了你丛雨准备了n张扑克牌作为牌堆,自牌顶向下数第x张牌作为乌龟,即“乌龟”的上方有x-1张牌,“乌龟”的下方有n-x张牌,抽中“乌龟”的将输掉这一轮比赛。每人每次可以同时在牌堆顶和牌堆底或者仅在牌堆顶或牌堆底其抽取任意张牌,至少抽1张牌。但若选择同时在牌堆顶和牌堆底抽牌,则抽牌数量需要相同。芳乃想知道在你的帮助下,她和丛雨都采取积极策略时,她自己是否一定能获胜。作为被丛雨邀请的一方,每轮游戏都是芳乃先抽。

因为看不见丛雨而误认芳乃罹患精神分裂的你在不由感叹红颜薄命的同时,决定尽全力帮助芳乃完成她的委托。

声明:本题中的所有角色在剧情发生时均已超过18岁。

题意:

n张牌,抽走第x张牌的输,第x张牌叫乌龟,乌龟上面有x-1张牌,乌龟下面有n-x张牌。每人每次抽排有两种方法,一是在上面或下面抽大于0的任意张牌,二是同时在上面和下面抽相同数目张牌。

思路:

威佐夫博弈。

我现在将这个题换个描述方式:有两堆牌,一堆有n-x张牌,另一堆有x-1张牌,取完最后一张的人赢。(因为取完最后一张,对手就可以取那张为在两堆牌内的乌龟了,所以你就赢了)

现在就很明显了,是威佐夫博弈,具体解释看我关于博弈论的一篇博客(虽然我就写了一半,懒得写下去了……手动狗头。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{   
    int t, x, n;
    cin>>t;
    while(t--){
        cin>>n>>x;
        int a = x - 1;
        int b = n - x;
        if(a > b)
            swap(a, b);
        if((int)((b - a) * (sqrt(5.0) + 1.0) / 2) == a)
            cout<<"ma la se mi no.1!\n";
        else
            cout<<"yo xi no forever!\n";
    }
    return 0;
}

F-成绩查询ing

题目描述

去年的新冠疫情爆发让众多大学生只能只能在家里上学,老师为了方便自己录入成绩和方便大家成绩查询,建立了一个录入和查询成绩的系统,能完成M次两种不同的查询,输入查询次数M,查询M次,每次首先输入查询的模式T,T为1时,输入同学的姓名Name,并依次输出同学的成绩Grade(0<=Grade<=100), 学号(0~1000000},性别(1/2),T为2时,输入成绩,输出有具体有哪些同学考到了这个分数,输出同学的Name,并要求按字典序输出,当没有同学为此分数时,则不输出。字典序,对于字符串,先按首字符排序,如果首字符相同,再按第二个字符排序,以此类推。

题意:

先录入学生的姓名成绩性别学号。然后进行m次查询,查询有两种情况,一种是输入姓名,输出成绩学号性别;另一种是输入成绩,输出有这个成绩的所有学生的名字,按字典序输出。

思路:

用一个结构体存成绩学号性别,通过map的结构体解决情况1,再用set的数组解决情况2.

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct ran
{
    int grade, gender, id;
}tr;
map<string, ran>mp;
set<string>se[120];
string s;
int main()
{
    int n;scanf("%d",&n);
    for(int i = 1; i <= n; i++){
        cin>>s;
        scanf("%d%d%d",&tr.grade, &tr.gender, &tr.id);
        mp[s] = tr;
        se[tr.grade].insert(s);
    }
    int m; scanf("%d",&m);
    while(m--){
        int t;scanf("%d",&t);
        if(t == 1){
            cin>>s;
            printf("%d %d %d\n",mp[s].grade,mp[s].id,mp[s].gender);
        }
        else{
            int x;scanf("%d",&x);
            for(auto y : se[x]){//用迭代器会超时,用auto不会
                cout<<y<<'\n';
            }
        }
    }
    return 0;
}

H-数羊

题目描述

憨憨小杨晚上睡不着觉,就开始数羊,她觉得一只一只数太慢了,突发奇想出了一种新的数羊方式,羊羊数量A(n,m)由两个整形变量n和m决定,计算方式如下:

第八届“图灵杯”NEUQ-ACM程序设计竞赛个人赛(同步赛)

现在给出n和m的值,请你帮小杨数数一共有多少只羊。

思路:

直接暴力枚举前十项,你就会发现规律

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int IntRead()
{
    char ch = getchar();
    int s = 0, w = 1;
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
            w = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        s = s * 10 + ch - '0',
        ch = getchar();
    }
    return s * w;
}
//int f(int x, int y)
//{
//    if(x == 1 && y == 0)
//    {
//        tr[x][y] = 2;
//        return tr[x][y];
//    }
//    else if(y >= 0 && x == 0)
//    {
//        tr[x][y] = 1;
//        return tr[x][y];
//    }
//    else if(y == 0 && x >= 2)
//    {
//        tr[x][y] = x + 2;
//        return tr[x][y];
//    }
//    else if(x >= 1 && y >= 1)
//    {
//        tr[x][y] = f(f(x - 1, y),y - 1);
//        return tr[x][y];
//    }
//    // return f(f(n - 1),m-1);
//}
ll q_pow(ll a, ll b)//第三列是2的n次方,所以要用快速幂取模
{
    ll ans = 1;
    while(b > 0)
    {
        if(b & 1)
        {
            ans = ans * a %998244353;
        }
        a = a * a % 998244353;
        b >>= 1;
    }
    return ans % 998244353;
}
int main()
{
    ll t, n, m;
    t = IntRead();
    while(t--)
    {
        n = IntRead();
        m = IntRead();
        if(m == 0)
        {
            if(n == 0)
                printf("1\n");
            else if(n == 1)
                printf("2\n");
            else
                printf("%ld\n",n + 2);
        }
        else if(m == 1)
        {
            if(n == 0)
                printf("0\n");
            else
                printf("%ld\n", 2 * n);
        }
        else if(m == 2)
        {
            if(n == 0)
                printf("0\n");
            else
                printf("%ld\n",q_pow(2,n));
        }
    }
    return 0;
}

I-买花

题目描述

情人节马上要到了,阳阳想送出n朵花给喜欢的妹妹,他打算提前开始买。但是,因为他有强迫症,所有的花要分k天买(k>1,即不能一天全买完),第一天他可以买任意朵花,之后每一天买花的数量为前一天的两倍,(如若第一天买4朵,第二天就要买8朵,以此类推)。

现在离情人节还有15天(k≤15),请你告诉阳阳,他能不能刚好买到n朵花。

题意:

要买n朵花,规则是第一天买任意朵,然后第二天比第一天多买一倍,最多买15天,花数是所有花的总和,问你能不能刚好买到n朵花。

思路:

买1朵的时候,花数f对于天数的函数为f(x) = 2 ^ x - 1.

所以我们只需要对n进行一次循环,看看能不能除尽这十五个数,如果可以就行,否则就不行。

这个题的输出我真的想喷,YES写成YE5,NO写成N0,这不认真看谁看的出来啊!这要是wa了就是在搞心态!

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll tr[100];
int main()
{
    ll t, n, x;
    cin>>t;
    while(t--)
    {
        x = 0;
        cin>>n;
        for(int i = 1; i <= 15; i++)
        {
            tr[i] = pow(2,i) - 1;
        }
        for(int i = 2; i <= 15; i++)
        {
            if(n % tr[i] == 0)
            {
                x = 1;
               // cout<<tr[i]<<endl;
                break;
            }
        }
        if(x)
            cout<<"YE5\n";
        else
            cout<<"N0\n";
    }
    return 0;
}

K-黑洞密码

题目描述

近些日子,某科学家接受到了来自外太空的神秘讯息,在经过了一段时间的研究后,科学家发现讯息是一个由字母和数字组成的字符串str,想要破译,需要通过一定的规则将字符串进行转换。规则如下:

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

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

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

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

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

思路:

四个一组进行处理就可以,反正也不长,怎么搞都行

#include<bits/stdc++.h>
using namespace std;
char f(char x, int y)//这是转变字符的函数
{
    if(x + y > 122)//看看是不是超过了z
    {
        x = 65 + y - (122 - x);
        return (char)x;
    }
    else if(x + y > 90 && x <= 90)//看看是不是超过了Z,注意要判断x<=90,因为Z与a就隔了7个字符,而y是属于[0,9]的,所以可能会有问题
    {
        x = 97 + y - (90 - x);
        return (char)x;
    }
    else
    {
        x += y;
        return (char)x;
    }
}
int main()
{
    int tr[20];
    char ar[20];
    string s;
    cin>>s;
    int a = 0, b = 0;
    for(int i = 0; i <= s.size(); i++)
    {
        if((s[i] >= 65 && s[i] <= 90) || (s[i] >= 97 && s[i] <= 122))
            ar[++a] = s[i];
        else
            tr[++b] = s[i] - '0';
    }
    for(int i = 4; i >= 1; i--)
    {
        cout<<f(ar[i],tr[i]);
    }
    for(int i = 8; i >= 5; i--)
    {
        cout<<f(ar[i],tr[i]);
    }
    for(int i = 12; i >= 9; i--)
    {
        cout<<f(ar[i],tr[i]);
    }
    for(int i = 16; i >= 13; i--)
    {
        cout<<f(ar[i],tr[i]);
    }
    cout<<'\n';
    return 0;
}

剩下的三个题,一个是A题切蛋糕,看题干感觉好长,就真懒得读

还有一个G题感觉是个“博弈”,但是又不太像博弈,有点思路但又感觉不太对,先放放

最后一个是L题,好像是个建图的题,没学过,等有空回来再补吧

我一定会回来了的……(逃

上一篇:第八届“图灵杯”NEUQ-ACM程序设计竞赛个人赛(同步赛)全题解


下一篇:第八届“图灵杯”NEUQ-ACM程序设计竞赛个人赛——I题 买花