第十四届蓝桥杯 2023 C/C++组 平方差

目录

题目:

题目描述:

题目链接:

思路:

核心思路:

第一种思路:

第二种思路:

坑点:

代码:

数学+找规律 O(n) 50分代码详解:

O(1)满分代码详解:

打表+找规律 O(1)代码详解:

补充平方差结论题目:

补充题目链接:

补充平方差结论代码详解:

河狸的数代码:

你不干?有的是帕鲁干!代码:


题目:

题目描述:

题目链接:

洛谷 P9231 [蓝桥杯 2023 省 A] 平方差

蓝桥云课 平方差

思路:

核心思路:

数学+找规律或打表+找规律

第一种思路:

第一种思路是用数论知识,用数学的方式推导出结论。如果之前有平方差的前置数学知识的话,这题很快就能解决,如果没有的话靠自己用数学知识也能推导出来,但是能不能想得到就是另一回事了

平方差结论:若a,b都是整数,则a^2-b^2一定是奇数或者4的倍数

a^2-b^2=(a+b)*(a-b),若a,b两个都是奇数,则a+b,a-b都是偶数,则(a+b)*(a-b)是4的倍数,因为偶数都有公共的质因子2,所以偶数*偶数一定是4的倍数; 若a,b一奇一偶,则a+b,a-b都是奇数,则(a+b)*(a-b)是奇数,因为奇数*奇数一定是奇数

再大白话地说:由题x=y^2-z^2,由平方差公式x=(y+z)*(y-z),y+z和y-z的奇偶性相同,5+3=8,5-3=2或5+4=9,5-4=1。所以x满足可以拆成两个奇偶性相同的数的乘积,如果x是奇数,可以拆成1乘以它本身。如果x是偶数,可以拆成两个偶数,所以应是4的倍数,拆成2和x/2。综上,答案是[l,r]区间的奇数和4的倍数的数的个数

第二种思路:

第二种思路是打表+找规律,这也是蓝桥杯常考的找规律的题的一种做法(后续我会总结蓝桥杯常见的找规律的题)。这里先简单提一嘴:找规律的题的特点:数据非常大,通常大于1e8,看上去非常吓人,肯定暴力跑不出来。但是一般有两个特性:1.周期性(结果在固定后重复)2.答案符合某种递推式。打表的意思就是先尝试暴力得到符合题目要求的前100、1000项来找规律。

坑点:

这题还有一个很阴的地方,即使你找到正确规律正常写也很容易踩坑。由题发现R最大为1e9,并且这题是编程题,也就是说正常一个for循环也会超时,只能拿50分。所以发现规律之后还得优化时间复杂度,把时间复杂度从O(n)优化到O(1)才能拿满分

代码:

数学+找规律 O(n) 50分代码详解:

#include<bits/stdc++.h> //这题主要考察数学+找规律,同时还有时间复杂度的问题 
using namespace std;    //这是我了解思路之后的第一个程序,时间复杂度O(n),只能拿50分,剩下50分超时 
                        //这题只有把时间复杂度优化到O(1)才能拿满分
int l,r;
int cnt;

int main()
{
	scanf("%d %d",&l,&r);
	for(int i=l;i<=r;i++) //遍历l到r的每一个整数,使用for循环,时间复杂度O(n) 
	{
		if(i%2==1) //判断是奇数 
		{
			cnt++;
		}
		if(i%4==0) //判断是4的倍数 
		{
			cnt++;
		}
	}
	cout<<cnt<<endl;
	return 0;
}
//数学+找规律思路:
//由题x=y^2-z^2,由平方差公式x=(y+z)*(y-z),y+z和y-z的奇偶性相同,5+3=8,5-3=2或5+4=9,5-4=1 
//所以x满足可以拆成两个奇偶性相同的数的乘积
//如果x是奇数,可以拆成1乘以它本身
//如果x是偶数,可以拆成两个偶数,所以应是4的倍数,拆成2和x/2
//综上,答案是[l,r]区间的奇数和4的倍数的数的个数 

O(1)满分代码详解:

#include<bits/stdc++.h> //时间复杂度O(1),不会超时能拿全部分 
using namespace std;

int l,r;

int f_jishu(int x) //求从[0,x]区间奇数的个数 
{
	return (x+1)/2; //传入0->0 ,1->1 ,2->1 ,3->2 ,4->2 ~~101->51 
}

int f_4(int x) //求[0,x]区间是4倍数的数的个数 
{
	return x/4; //显然传入3->0 ,4->1 ,7->1 ,8->2 ~~101->25 
}

int main()
{
	scanf("%d %d",&l,&r);
	cout<<f_jishu(r)+f_4(r)-f_jishu(l-1)-f_4(l-1); //求[l,r]区间奇数和是4倍数的数的个数 
	return 0;
}

打表+找规律 O(1)代码详解:

#include<bits/stdc++.h>  //蓝桥杯常考的找规律的题,由题R最大为10^9,数据范围太大,暴力肯定不行 
using namespace std;  //平方差结论:若a,b都是整数,则a^2-b^2一定是奇数或者4的倍数
//a^2-b^2=(a+b)*(a-b),若a,b两个都是奇数,则a+b,a-b都是偶数,则(a+b)*(a-b)是4的倍数,因为偶数都有公共
//的质因子2,所以偶数*偶数一定是4的倍数; 若a,b一奇一偶,则a+b,a-b都是奇数,则(a+b)*(a-b)是奇数,因为
//奇数*奇数一定是奇数
//如果知道这个前置结论:题目就转换为L到R之间是奇数或者是4的倍数的数总和 

int l,r;           //这题还有一个比较阴的地方就是,知道规律之后,用一次for还是会超时 
                   //时间复杂度O(n),但是R最大为10^9,O(n)的话最大为10^9 >10^8,必须优化到O(1) 
int f_jishu(int x)
{
	return (x+1)/2;
}

int f_4(int x)
{
	return x/4;
}

int main()
{
	scanf("%d %d",&l,&r);
	cout<<f_jishu(r)+f_4(r)-f_jishu(l-1)-f_4(l-1);
	return 0;
}

//如果不知道这个前置结论,做的时候也没推出结论,那别无他法的时候还是先暴力枚举100个看看有没有规律 
//int main()
//{
//	for(int i=1;i<=100;i++)  //暴力从1枚举到100找找看规律 
//	{
//		for(int j=0;j<=100;j++)  //i=k^2-j^2,由于搞不清到什么程度找不到j和k才算不满足要求 
//		{
//			for(int k=j+1;k<=100;k++) //所以先把i和k的终止条件先设大一点试试看 
//			{
//				if(i==k*k-j*j)
//				{
//					cout<<i<<' ';
//				}
//			}
//		}
//	}
//	return 0;
//}
//1 3 4 5 7 8 9 9 11 12 13 15 15 16 16 17 19 20 21 21 23 24 24 25 25 27 27 28 29 31 32 32 33 33 
//35 35 36 36 37 39 39 40 40 41 43 44 45 45 45 47 48 48 48 49 49 51 51 52 53 55 55 56 56 57 57 
//59 60 60 61 63 63 63 64 64 64 65 65 67 68 69 69 71 72 72 72 73 75 75 75 76 77 77 79 80 80 80 
//81 81 81 83 84 84 85 85 87 87 88 88 89 91 91 92 93 93 95 95 96 96 96 96 97 99 99 99 100 100
//输出数据发现有的数会重复输出几次,是因为j,k设置的终止条件满足能找到多种组合来平方差得到i
//但是毕竟是没有其他思路办法的无奈之举,所以把重复的数删一下再找找有没有规律 
//1 3 4 5 7 8 9 11 12 13 15 16 17 19 20 21 23 24 25 27 28 29 31 32 33 35 36 37 39 40 41 43 44 45 
//47 48 49 51 52 53 55 56 57 59 60 61 63 64 65 67 68 69 71 72 73 75 76 77 79 80 81 83 84 85 87 88
//89 91 92 93 95 96 97 99 100
//这些就是删去重复的满足题目要求的解了,通过这些数勉强能看出奇数和4的倍数能满足要求,但是有点抽象

补充平方差结论题目:

补充题目链接:

蓝桥云课 河狸的数(平方差结论)

蓝桥云课 你不干?有的是帕鲁干!(平方差结论拓展)

补充平方差结论代码详解:

河狸的数代码:

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+10;  //多开一点,防止数组越界 

int n;
long long a[N];      //由题整数最大为10^18,会爆int,一定要开long long 

int main()
{
  cin>>n;
  for(int i=0;i<n;i++)
  {
    scanf("%lld",&a[i]);
    if(a[i]%4==0||a[i]%2==1)  //平方差结论 
    {
      printf("Yes ");
    }
    else
    {
      printf("No ");
    }
  }
  return 0;
}

你不干?有的是帕鲁干!代码:

#include<bits/stdc++.h> //这题是平方差结论的拓展,由题要求x可以被表达成两个连续正奇数的平方之差 
using namespace std;    //两个连续正奇数:2*k-1 2*k+1(从1开始,所以k=1,2,3...)
//由平方差公式:(2*k+1)^2-(2*k-1)^2=(2*k+1+2*k-1)*(2*k+1-2*k+1)=8*k  k=1,2,3...
//所以如果x能被表达为两个连续正奇数的平方之差,则x是8的倍数,注意由题x从0开始,但是x不能为0,因为k从
//1开始,即最小满足能被表达成两个连续正奇数的平方之差的数是8,所以判断条件是x!=0&&x%8==0,输出Yes
//题目还要求从小到大输出这两个连续正奇数,即先求k=x/8,a=2*k-1,b=2*k+1,输出a b即可 

long long x;  //由题输入的非负整数x最大为10^18,会爆int,要开long long 
int n;

int main()
{
	cin>>n;
	while(n--)
	{
		scanf("%lld",&x);
		if(x!=0&&x%8==0)   //由结论判断x是否能被表达成两个连续正奇数的平方之差 
		{
			printf("Yes\n");
			long long k=x/8;   //注意x最大为10^18,x要开long long,那么k,a,b都要开long long 
			long long a=2*k-1; //因为k,a,b都是很小的运算,如果x爆int,则k,a,b后续运算也会爆int 
			long long b=2*k+1;
			printf("%lld %lld\n",a,b);
		}
		else
		{
			printf("No\n");
		}
	}
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值