“迎新春,过大年”多校程序设计竞赛题解

目录

A、新春游戏之数学系列

B、团圆饭

C、龙年吉祥

​​​​​​​ K、集福卡


A、新春游戏之数学系列

Bingbong的数学竞赛又考砸了,这也就意味着压岁钱又要减半了,但是!心地善良的朵朵姐姐决定再给他一次机会,给他出了一道数学题,如果他能正确解答,那么压岁钱就翻上一番。题目如下:

给你一个长度为 n 的整数序列a1,a2,...,an。

求:   

其中popcount(x)popcount(x)popcount(x)表示整数 xxx 在二进制下的 111 的个数,gcdgcdgcd表示两个数的最大公约数。

BingbongBingbongBingbong的由于竞赛考砸,心情还处于一个极其低落的状态,为了能拿到更多的压岁钱,他决定请教数学高手的你来帮助他解决这个问题。

输入描述:

第一行一个整数 n。 第二行n个整数,表示a1,a2,...,an。

数据范围:1≤n≤106,1≤ai,∑ai≤5×107具体看题

输出描述:

一行一个整数,表示答案。
示例1
输入
5 666 230 120 4 12

输出

10672

思路:这只是我的个人意见,首先如果我们暴力求的话,时间肯定会过,时间为n^2,肯定不行。我们要优化代码。

1.我们先考虑优化cin、cout。1.解除iostream库与stdio库同步关系;2.解除了cin和cout之间的绑定。默认情况下,cin是与cout绑定的,当cin读取数据之前,cout的缓冲区都会被刷新3.防止cout受其他流的影响。

2.去重,用map,记录一个元素在本次代码出现多少次,然后再相乘。像2出现了3次,那在2这个数用3*2,可以减少时间,遍历就用auto i:mp;还可以用 __builtin_popcount(x)这个函数。

我介绍一下 __builtin_popcount()这个函数:

__builtin_popcount() 是一个编译器内置函数,用于计算一个无符号整数的二进制表示中的 1 的个数。该函数在很多编译器(如GCC和Clang)中提供,旨在提供高效的位计数操作。

函数原型为:int __builtin_popcount(unsigned int x);

具体内容看代码

代码:

#include<bits/stdc++.h>
#include<map>
#include<algorithm>
#define int long long
using namespace std;
const int N=1e6+10;
const int mod=998244353;
int abb[N]={0};
map<int,int>aqq;
int gcd(int a,int b)
{
   if(b==0)
        return a;
    else
        return gcd(b,a%b);
}
signed main()
{
    cin.tie(nullptr)->sync_with_stdio(false);
    int n;cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>abb[i];
        aqq[abb[i]]++;
    }
    int x=0;
    for(auto i : aqq)
    {
        for(auto j : aqq)
        {
            x+=(i.second * j.second *( gcd(i.first,j.first) % mod * 
            (__builtin_popcount(i.first) + __builtin_popcount(j.first))))%mod;
            x%=mod;
        }
    }
    cout<<x<<endl;
    return 0;}

B、团圆饭

新年团圆饭是中国传统文化中的重要节日活动之一,通常在农历新年期间举行。这一传统活动是家庭成员团聚在一起,共同享用丰盛的晚餐,象征着团圆和幸福。在这一天,家人们会精心准备各种美味佳肴,如鱼、猪肉、鸡、蔬菜和汤等,以表达对新一年的美好祝愿。

无论身在何处,新年团圆饭都是中国人心中最温馨、最难忘的时刻之一。这一传统活动不仅强调了家庭的重要性,也承载了对新年的美好祝愿和对未来的期许。

2024年到了,Bingbong家里来了许多亲戚坐成一排,一共有n个人(共n个座位),Bingbong想要从亲戚中挑选出若干个人和他一起玩游戏,为了使座位上看起来不过于稀疏,所以他挑选出来的人中应满足任意两个人的座位互不相邻,Bingbong还想着玩他的游戏,所以他请你来帮助他计算可选的总方案数目,由于结果可能很大,所以请把结果对109+71取模后再输出。

注意:Bingbong是一个习惯了孤独的人,所以他可以自娱自乐~

思路:我们可以发现这是一个类似于f(n)=f(n-1)+f(n-2)规律;然后用递归肯定不行,因为时间复杂度是2^n,然后用动态规划,时间复杂度n,也不可以,因为n为10的18次方,也会炸。所以可以用矩阵快速幂,时间复杂度logn。下面说说什么是矩阵快速幂:

快速矩阵幂是一种高效计算矩阵的幂的方法,它可以在较短的时间内计算出较大的幂次。这在很多数学和计算机科学问题中都有应用,比如图论、动态规划等。

快速矩阵幂的基本思想是通过将指数进行二进制拆分,将矩阵的幂次表示为多个较小的幂次相乘。具体步骤如下:

1.将指数 n 转换为二进制形式,例如 n = 13 转换为二进制为 1101。

2.初始化结果矩阵为单位矩阵,即 A = I。

3.从二进制的最低位开始,从右向左遍历二进制数。

4.如果当前位为 1,则将结果矩阵与原始矩阵相乘,即 A = A * M。

5.将原始矩阵平方,即 M = M * M。

6.继续遍历下一位,直到遍历完所有位数。

7.最终得到的结果矩阵 A 即为原始矩阵 M 的幂次。

这种方法的时间复杂度为 O(log n),相比直接进行 n 次矩阵乘法运算的时间复杂度 O(n) 更加高效。例子:2^53;53是110101,所以可以写成2^32*2^16*2^4*2^1;累乘1*2^1,下一次乘2^4(2^1*2^1)(2^1*2^1);这就是第5步的道理,如果还没有看懂的话,可以去看一下b站。

具体详情请看代码:

代码:

#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
#define int long long int
const int mod = 1e9 + 7;
int aqq[2][2] =
{
    {0,1},
    {1,1}
};
int res[2] = { 2,3 };//第一个人
void abb(int A[], int B[][2])
{
    int ass[2] = { 0 };
    for (int i = 0; i < 2; i++)
        for (int j = 0; j < 2; j++)
            ass[i] += A[j] * B[i][j] % mod;
    A[0] = ass[0] % mod;
    A[1] = ass[1] % mod;
}
void arr(int A[][2], int B[][2])
{
    int ass[2][2] = { 0 };
    for (int i = 0; i < 2; i++)
        for (int j = 0; j < 2; j++)
            for (int k = 0; k < 2; k++)
            {
                ass[i][j] += (A[i][k] % mod) * (B[k][j] % mod) % mod;
            }
    for (int i = 0; i < 2; i++)
        for (int j = 0; j < 2; j++)
            A[i][j] = ass[i][j] % mod;
}
signed main()
{
    int sum = 1, n;//可以自己玩,所以起始位1
    cin >> n;
    if (n == 0)
    {
        cout << sum << endl;
        return 0;
    }
    n--;
    while (n)
    {
        if (n & 1)
        {
            abb(res, aqq);
        }
        arr(aqq, aqq);
        n >>=1;
    }
    cout << res[0] << endl;
    return 0;
}

C、龙年吉祥

十二生肖是中国传统文化中的一个重要概念,它源自于中国古代的生肖文化,用来纪年和纪月。十二生肖分别是鼠、牛、虎、兔、龙、蛇、马、羊、猴、鸡、狗和猪,每个生肖代表了一种动物,并且与特定的年份相关联。据说这些生肖的顺序是由伏羲氏所定,而后被传承下来。

每个生肖都有其独特的象征意义和特点。比如,鼠代表着聪明和机智,牛象征着坚毅和勤劳,虎代表着勇猛和力量,兔则象征着温和和灵巧,龙代表着神秘和权威,蛇代表着智慧和灵性,马象征着勇敢和自由,羊代表着温和和善良,猴则象征着活泼和聪慧,鸡代表着勤劳和勇敢,狗象征着忠诚和正直,猪则代表着善良和丰富。

在中国的传统文化中,人们相信十二生肖与每个人的命运息息相关,因此十二生肖也被广泛应用于占卜、风水、命理等方面。此外,每逢生肖年的到来,人们都会举行各种庆祝活动,以及相应的传统习俗,以祈求好运和平安。十二生肖不仅是中国文化的重要组成部分,也是人们生活中不可或缺的一部分。

最近BingbongBingbongBingbong突然对十二生肖起了兴趣,通过了解分别为鼠牛虎兔龙蛇马羊猴鸡狗猪。

他知道202420242024年是龙年,他想知道NNN年后是什么生肖年?

当N≥0N\geq0N≥0时则代表在202420242024年基础上增加∣N∣\left| N \right|∣N∣年,例如N=2N=2N=2,则代表是202620262026年。

当N<0N<0N<0时则代表在202420242024年基础上减少∣N∣\left| N \right|∣N∣年,例如N=−5N=-5N=−5,则代表是201920192019年。

十二生肖的单词附表如下:

输入描述:

第一行一个整数T。代表数据组数

接下来每行一个整数N。

数据范围:具体看题目

输出描述:

一个字符串,代表N年后是什么年。

示例1

输入、

2

3

-5

输出

Go

Pig

思路:首先,用结构体输出string。因为N属于1e18次方,所以不能for,要对12求摸。然后分四情况,以此考虑。首先,mod==0,mod>0&&mod<=7,mod<0&&mod>=-4,mod<-4;这四种情况,用if判断就OK了。详情请看代码,欢迎大佬用其他思路来一起探讨。

代码:

#include<iostream>
using namespace std;
struct num{
  string s;  
}abb[12];
int main()
{
    abb[0].s="Rat";
    abb[1].s="Ox";
    abb[2].s="Tiger";
    abb[3].s="Rabbit";
    abb[4].s="Dragon";
    abb[5].s="Snake";
    abb[6].s="Horse";
    abb[7].s="Goat";
    abb[8].s="Monkey";
    abb[9].s="Rooster";
    abb[10].s="Dog";
    abb[11].s="Pig";
    int n;cin>>n;
    for(int i=1;i<=n;i++)
    {
        long long int num=0;cin>>num;
        num%=12;
        if(num>=-4&&num<=7)
            cout<<abb[4+num].s<<endl;
        else if(num>7)
            cout<<abb[num-8].s<<endl;
        else 
            cout<<abb[12+4+num].s<<endl;
    }
    return 0;
}

 K、集福卡

新年到了,BingbongBing又来集齐五福了,五福分别为爱国福,富强福,友善福,和谐福以及敬业福。往年的敬业福的获得概率可以说是“难上加难”,为了让用户有更好的体验,于是今年主办方的获取方法做出了一些调整,具体规则如下:

1.每1套爱国+富强+友善+和谐可以换取1张敬业福

2.每1张敬业福可以换取1套爱国+富强+友善+和谐

现在Bingbong分别有爱国,富强,友善,和谐,以及敬业的卡A,B,C,D,E张,他想知道他最多能凑齐多少套五福? 

思路:2种情况:第一种情况是敬业福是最小的,需要执行爱国+富强+友善+和谐换取敬业福,这个时候用他们四张最小的-敬业福的张数在/2+敬业福的张数;第二种情况是敬业福不是最小的,敬业福可以换取爱国+富强+友善+和谐,用敬业福-最小的+剩余的/2;具体代码看下

代码:

#include<iostream>
#include<cmath>
#include<algorithm>
#define int long long
using namespace std;
signed main()
{
    int n;cin>>n;
    for(int i=1;i<=n;i++)
    {
        int abb[6]={0};
        int sum=0,m=0;
        for(int j=1;j<=5;j++)
        {
            cin>>abb[j];
            if(j==1)
                m=abb[j];
            else 
                m=min(m,abb[j]);
        }
        sum+=m;
        for(int j=1;j<=5;j++)
        {
            abb[j]-=m;
        }
        if(abb[5]==0)
        {
            sort(abb+1,abb+5);
            sum+=(abb[1]/2);
            cout<<sum<<endl;
        }
        else 
        {
            sum+=(abb[5]/2);
            cout<<sum<<endl;
        }
    }
    return 0;
}

  • 14
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值