蓝桥杯31天冲刺打卡(Day9)

        Hallo,小伙伴们!没错!又是我——永遇乐,不知不觉就已经第九天了,今天我继续带领你们解答蓝桥杯的题目。俗话说得好:冰冻三尺非一日之寒,所以我们做题也要持之以恒呀!各位小伙伴,你们准备好与我共同继续前行了吗?

        今天有四道题目,也不算太难,没涉及什么算法,主要是模拟和数学。让我们遨游于题海中吧!

A 最大乘积

回顾老题:

        博主看到的时候就心想:这怎么和昨天的神奇算式这么像!!!

神奇算式   原题及题解传送门

解析:

        打眼儿一看,这是一道模拟题,这道题目要求我们式子左边的两个数共包含1~9,9位数字,式子右边要求我们也是9位数(1~9不同的数字),所以我们需要用数组判定两边的数位中是否出现相同的数,而且我们看到例子中的9*87146325 = 784316925,看到这个乱序的数,我们想到了什么?没错!就是C++中的全排列函数,到这里我们成功了一大半了。题目问的是乘积最大是多少,我们当然会不自主地想到max函数。到这里,本题就已经分析完毕,接下来让我们看看具体如何实现吧!

代码:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
LL maxn;
int a[10]= {0,1,2,3,4,5,6,7,8,9},b[10];//a用于全排列,b用于判断数位上的数是否有重复 

bool check() {
    for(int i=1; i<=9; i++)
    if(b[i]==0) return false;
    return true;
}

int main() {
    do {
        for(int i=1; i<9; i++) {
            LL s1=0,s2=0;
            memset(b,0,sizeof(b));//清零

            for(int j=1; j<=i; j++)
                s1=s1*10+a[j];//从9位数中的8处地方的某一处划分成两个数

            for(int j=9; j>i; j--)
                s2=s2*10+a[j];
            
            //cout<<s1<<" "<<s2<<endl;
            LL s3=s1*s2;
            LL sum=s3;
            while(sum) {
                if(b[sum%10]==0) b[sum%10]++;
                else break;
                sum/=10;
            } 
            if(check()) 
            maxn=max(maxn,s3);
            //cout<<s1<<" * "<<s2<<" = "<<s3<<endl;
        }
    } while(next_permutation(a+1,a+10));
    cout<<maxn;
    return 0;
}

B 阶乘约数

解析:

         小伙伴们,看到100!的瞬间傻眼了吧?博主当时也傻了,这100!多大不说,还要我求有几个正约数,这该咋求啊?算出来再一个一个去求?那根本不可行呀!long long也只能存到20!,所以我们肯定不用这个方法,所以我在网上查了一下,发现两个定理:唯一分解定理和约数和定理。

        唯一分解定理:

 大概意思就是任意大于1的数都能通过素数相乘而得到。

        约数和定理:对于一个大于1正整数n可以分解质因数:n=p1^a1*p2^a2*p3^a3*…*pk^ak,则由 约数个数定理可知n的正约数有(a₁+1)(a₂+1)(a₃+1)…(ak+1)个。

        通过以上两个定理,这题就变得非常简单了。100!=1*2*......*100,那么我们可以将其中的非素数通过分解质因数变成素数,这样就符合唯一分解定理了,然后用数组存储这些素数的个数,到最后用一个for配合约数和定理便大功告成了。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int a[100];
LL s=1;

int main() {
    for(int i=2; i<=100; i++) { //唯一分解定理
        int j=i;
        for(int k=2; k<=j; k++) { //分解质因数
            while(j%k==0) {
                a[k]++; //存储分解出素数的个数
                j/=k;
            }
        }
    }
    for(int i=2; i<=100; i++)
    if(a[i]>0) s=s*(1+a[i]); //约数和定理
    cout<<s;
    return 0;
}

 

C 含2天数

解析:

        这是一道非常简单的模拟题,就是枚举1900年1月1日到9999年12月31日的所有日期,并判定年月日中是否含2这个数字,注意判断闰年。

代码:

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int date[]= {0,31,28,31,30,31,30,31,31,30,31,30,31};
int sum=0;

bool check(int n) {
    while(n) {
        if(n%10==2) return true;
        n/=10;
    }
    return false;
}

int main() {
    for(int year=1900; year<=9999; year++) {
        if(year%400==0 || (year%4==0 && year%100!=0)) date[2]=29; //闰年
        else date[2]=28;
        for(int month=1; month<=12; month++)
            for(int day=1; day<=date[month]; day++) {
                if(check(year) || check(month) || check(day)) sum++;
            }
    }
    cout<<sum;
    return 0;
}

D k倍区间

输出:6

解析:

        这道题是一道典型的前缀和和同余定理的题目,你也可以理解为是一道数学题。小伙伴们可能并不知道前缀和和同余定理是什么,下面我给大家科普一下。

        一维前缀和:假设x1,x2,...,xn是元素,y1,y2,...yn是存储前缀和的容器,那么y1=x1, y2=x1+x2, y3=x1+x2+x3,y4=x1+x2+x3+x4,...yn=x1+x2+x3+...+xn,像这样关系的就是一维前缀和。为什么我要说这是一维前缀和呢?因为还有二维的,三维的,甚至n维的。

        同余定理:《离散数学》中是这样定义的: 设m是正整数,a和b是整数.如果m | a-b,则称a模m同余于b,或a与b模m同余.其充要条件是:a与b除以m的余数相等,即a mod m = b mod m。

         题目说到连续子序列,也就是[1,5],[1,2],[2,3],[10,100]......形如这样的序列。而前缀和为什么适用于本题,例如:[1,5]则S5,[5,9]则S9-S4,这里面的Si都是前缀和(前i个数相加)。题中说到连续子序列之和是k的倍数,则称其为k倍区间,问有几个。

        用样例举例吧。样例输入5个数,k=2,也就是连续子序列之和要是2的倍数。前缀和就是s1=1, s2=3, s3=6, s4=10, s5=15,那么哪里用到了同余定理呢?

        我们仔细观察一下,s1,s2,s5都是除以2余1,s3,s4除以2余0,这说明什么?说明:s2-s1, s5-s1, s5-s2, s3, s4, s4-s3 都是k倍区间。s2-s1=2, s5-s1=14,正好就是2的倍数。

        因此我们可以先求得前缀和,再找出前缀和相同的余数有几个,再将其分类相加。这里注意一下,s1,s2,s5是余1,共3个,即(n-1)*n/2个;而s3,s4余0,也就是本身也是2的倍数,所以也有3个,即(n+1)*n/2个。

代码:

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;

int n,k;
int a[100005];
LL s[100005],sum[100005],cnt;

int main() {
	cin>>n>>k;
	
	for(int i=1; i<=n; i++) {
		cin>>a[i];
		s[i]=s[i-1]+a[i]; //前缀和 
	}

	for(int i=1; i<=n; i++)
		sum[s[i] % k]++; // 算同余的个数 
		
	for(int i=0; i<k; i++) {
		if(i==0) sum[i]++; //余数为0需+1
		cnt+=(sum[i]*(sum[i]-1))/2; 
	}
	
	cout<<cnt;
	return 0;
}

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_谦言万语

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值