1.蓝桥杯2024年第十五届决赛真题- 蚂蚁开会
题目描述
二维平面上有 n 只蚂蚁,每只蚂蚁有一条线段作为活动范围,第 i 只蚂蚁的活动范围的两个端点为 (uix, uiy),(vix, viy)。现在蚂蚁们考虑在这些线段的交点处设置会议中心。为了尽可能节省经费,它们决定只在所有交点为整点的地方设置会议中心,请问需要设置多少个会议中心?
输入格式
输入共 n + 1 行。第一行为一个正整数 n。后面 n 行,每行 4 个由空格分开的整数表示 uix, uiy, vix, viy。
输出格式
输出共 1 行,一个整数表示答案。
样例输入
复制
4 0 0 4 4 0 4 4 0 2 0 0 4 2 1 2 3
样例输出
复制
2
提示
【样例说明】
所有线段之间共有 3 个不同的交点:(0, 4),(43,43),(2, 2), 其中整点有 2 个:(0, 4),(2, 2)。
【评测用例规模与约定】
对于 20% 的评测用例,保证 0 ≤ uix, uiy, vix, viy ≤ 100。对于 100% 的评测用例,保证 n ≤ 500,0 ≤ uix, uiy, vix, viy ≤ 10000,保证任意蚂蚁的活动范围不会退化成一个点,不保证任意两条线段之间交点数量有限。
AC
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PI;
map<PI,int>M;
map<PI,bool>vis;
int gcd(int a,int b){
if(b==0) return a;
else return gcd(b,a%b);
}
int n;
int ans = 0;
int main(){
cin >> n;
for(int i = 1 ; i <= n ; i ++){
int x1,y1,x2,y2;
cin >> x1 >> y1 >> x2 >> y2;
int g = gcd(abs(x1-x2),abs(y1-y2));
int ax = (x2-x1)/g;
int ay = (y2-y1)/g;
while(x1 != x2 || y1 != y2){
M[PI(x1,y1)]++;
if(M[PI(x1,y1)] >= 2 && !vis[PI(x1,y1)]){
ans ++;
vis[PI(x1,y1)] = 1;
}
x1 += ax;
y1 += ay;
}
M[{x2,y2}]++;
if(M[PI(x2,y2)] >= 2&& !vis[PI(x2,y2)]){
ans ++;
vis[PI(x2,y2)] = 1;
}
}
cout << ans <<endl;
return 0;
}
2.蓝桥杯2024年第十五届决赛真题-立定跳远
题目描述
在运动会上,小明从数轴的原点开始向正方向立定跳远。项目设置了 n 个检查点 a1, a2, . . . , an 且 ai ≥ ai−1 > 0。小明必须先后跳跃到每个检查点上且只能跳跃到检查点上。同时,小明可以自行再增加 m 个检查点让自己跳得更轻松。
在运动会前,小明制定训练计划让自己单次跳跃的最远距离达到 L,并且学会一个爆发技能可以在运动会时使用一次,使用时可以在该次跳跃时的最远距离变为 2L。小明想知道,L 的最小值是多少可以完成这个项目?
输入格式
输入共 2 行。
第一行为两个正整数 n, m。
第二行为 n 个由空格分开的正整数 a1, a2, . . . , an。
输出格式
输出共 1 行,一个整数表示答案。
样例输入
复制
5 3 1 3 5 16 21
样例输出
复制
3
提示
【样例说明】
增加检查点 10, 13, 19,因此每次跳跃距离为 2, 2, 5, 3, 3, 3, 2,在第三次跳跃时使用技能即可。
【评测用例规模与约定】
对于 20% 的评测用例,保证 n ≤ 102,m ≤ 103,ai ≤ 103。
对于 100% 的评测用例,保证 2 ≤ n ≤ 105,m ≤ 108,0 < ai ≤ 108。
AC:
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int a[N];
int n, m;
bool check(int mid)
{
int cnt = m;
for (int i = 1; i < n; i++)
{
int t = a[i] - a[i - 1];
if (mid < t)
{
if (t % mid == 0)
{
cnt -= (t / mid - 1);
if (cnt < 0) return false;
}
else
{
cnt -= (t / mid);
if (cnt < 0) return false;
}
}
}
return true;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
m++;
int l = 1, r = 1e8 + 1;
while (l < r)
{
int mid = (l + r) >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
cout << r;
return 0;
}
3.蓝桥杯2024年第十五届决赛真题-最小字符串(中等偏下)
题目描述
给定一个长度为 N 且只包含小写字母的字符串 S,和 M 个小写字母c1, c2, . . . , cM。现在你要把 M 个小写字母全部插入到字符串 S 中,每个小写字母都可以插入到任意位置。
请问能得到的字典序最小的字符串是什么?
输入格式
第一行包含两个整数 N 和 M。
第二行包含一个长度为 N 的字符串 S。
第三行包含 M 个小写字母 c1, c2, . . . , cM。
输出格式
输出一个长度为 N + M 的字符串代表答案。
样例输入
复制
4 3 abbc cba
样例输出
复制
aabbbcc
提示
【评测用例规模与约定】
对于 20% 的评测用例,M = 1。对于 100% 的评测用例,1 ≤ N, M ≤ 105。
AC:
#include <bits/stdc++.h>
using namespace std;
int n, m;
string s, t, ans;
int main()
{
cin >> n >> m;
cin >> s >> t;
sort(t.begin(), t.end()); //将m字符串进行排序
int j = 0;
for (int i = 0; i < n; i++)
{
while (j < m && s[i] > t[j])
{
ans += t[j];
j++;
}
ans += s[i];
}
for (int i = j; i < m; i++)
ans += t[i];
cout << ans << endl;
return 0;
}
4.蓝桥杯2024年第十五届决赛真题-数位翻转(中等)
小明创造了一个函数 f(x) 用来翻转 x 的二进制的数位(无前导 0)。比如f(11) = 13,因为 11 = (1011)2,将其左右翻转后,变为 13 = (1101)2;再比如f(3) = 3,f(0) = 0,f(2) = f(4) = f(8) = 1 等等。
小明随机出了一个长度为 n 的整数数组 {a1, a2, ..., an},他想知道,在这个数组中选择最多 m 个不相交的区间,将这些区间内的数进行二进制数位翻转(将ai 变为 f(ai))后,整个数组的和最大是多少?
输入格式
输入共 2 行。
第一行为两个正整数 n, m。
第二行为 n 个由空格分开的整数 a1, a2, ..., an。
输出格式
输出共 1 行,一个整数表示答案。
样例输入
复制
5 3 11 12 13 14 15
样例输出
复制
67
提示
【样例说明 1】只翻转 a1,和为 13 + 12 + 13 + 14 + 15 = 67。
再比如:
【样例输入 2】6 223 8 11 19 16 35
【样例输出 2】134
【样例说明 2】翻转区间 [a3, a4] 和 [a6],和为 23 + 8 + 13 + 25 + 16 + 49 = 134。
【评测用例规模与约定】
对于 20% 的评测用例,保证 n, m ≤ 20。
对于 100% 的评测用例,保证 n, m ≤ 1000,0 ≤ ai ≤ 109。
AC:
#include#includeusing namespace std;
typedef long long ll;
const int maxn=1e3+100;
ll a[maxn];
ll b[maxn];
ll tmp[100000];
ll dp[maxn][maxn][2];//1是这个变化,0是不变化
ll f(ll x){
ll res = 0;
while (x > 0) {
res = (res << 1) | (x & 1);// 将x的最后一位加到res的最后
x >>= 1;// 将x右移一位
}
return res;
}
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i>a[i];
b[i]=f(a[i]);
}
for(int i=1;i<=n;i++){
dp[i][0][0]=dp[i-1][0][0]+a[i];
for(int j=1;j<=m;j++){
dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j][1])+a[i];
dp[i][j][1]=max(dp[i-1][j-1][0],dp[i-1][j][1])+b[i];
}
}
ll ans=0;
for(int j=0;j<=m;j++){
ans=max(ans,max(dp[n][j][0],dp[n][j][1]));
}
cout<<ans<<endl;
}
5.蓝桥杯2022年第十三届省赛真题-统计子矩阵
题目描述
给定一个 N × M 的矩阵 A,请你统计有多少个子矩阵 (最小 1 × 1,最大 N × M) 满足子矩阵中所有数的和不超过给定的整数 K?
输入格式
第一行包含三个整数 N, M 和 K.
之后 N 行每行包含 M 个整数,代表矩阵 A.
输出格式
一个整数代表答案。
样例输入
复制
3 4 10 1 2 3 4 5 6 7 8 9 10 11 12
样例输出
复制
19
提示
满足条件的子矩阵一共有 19,包含:
大小为 1 × 1 的有 10 个。
大小为 1 × 2 的有 3 个。
大小为 1 × 3 的有 2 个。
大小为 1 × 4 的有 1 个。
大小为 2 × 1 的有 3 个。
对于 30% 的数据,N, M ≤ 20. 对于 70% 的数据,N, M ≤ 100.
对于 100% 的数据,1 ≤ N, M ≤ 500; 0 ≤ Ai j ≤ 1000; 1 ≤ K ≤ 250000000.
AC:
#include<iostream>
using namespace std;
const int N = 510 , M = 510;
int s[N][M];
long long n , m , k , cnt = 0; //cnt要为long long,不然会报错
int main()
{
cin>>n>>m>>k;
for(int i = 1;i <= n;i ++)
for(int j = 1;j <= m;j ++)
{
cin>>s[i][j];
s[i][j] += s[i-1][j]; //求列的前缀和
}
for(int i1 = 1;i1 <= n;i1 ++)
for(int i2 = i1;i2 <= n;i2 ++)
for(int l = 1 , r = 1 , sum = 0;r <= m; r++)
{//左右边界及当前子矩阵总和
sum += s[i2][r] - s[i1 - 1][r]; //向右累加
while(sum > k)
{//超出k则从左边开始缩减
sum -= s[i2][l] - s[i1 - 1][l];
l++;
}
cnt += r - l + 1; //以left为起始,依次扩一列作为一个子矩阵
}
cout<<cnt<<endl;
return 0;
}
6. 蓝桥杯2022年第十三届省赛真题-积木画
题目描述
小明最近迷上了积木画,有这么两种类型的积木,分别为 I 型(大小为 2 个单位面积)和 L 型(大小为 3 个单位面积):
同时,小明有一块面积大小为 2 × N 的画布,画布由 2 × N 个 1 × 1 区域构成。小明需要用以上两种积木将画布拼满,他想知道总共有多少种不同的方式? 积木可以任意旋转,且画布的方向固定。
输入格式
输入一个整数 N,表示画布大小。
输出格式
输出一个整数表示答案。由于答案可能很大,所以输出其对 1000000007 取模后的值。
样例输入
复制
3
样例输出
复制
5
提示
五种情况如下图所示,颜色只是为了标识不同的积木:
对于所有测试用例,1 ≤ N ≤ 10000000.
解题思路:
思路:a[i][0]:i列积木的堆法,a[i][1]:i列多一块小方格的堆法。
如:
或者
不管这三块是怎么放的,都叫a[3][0].
如果在此基础上右边多一个小方格就叫a[3][1],注意小方格可以在第四列上一行也可以在第四列下一行(不好找图就不给了,自行想象)
给出动态规划方程:
a[i][0]=(a[i-2][0]+a[i-2][1]+a[i-1][0])%mod;
a[i][1]=(a[i-1][0]*2+a[i-1][1])%mod;
画布大小为i的排法,即a[i][0]的值:
- 先考虑少一块I型积木的排法,即a[i-1][0],这时加上一块I型积木就行了,
- 再考虑少一块L型积木的排法,即a[i-2][1],这时加上一块L型积木就行了,
- 考虑少两块I型积木的排法,即a[i-2][0],加上两块I型积木,注意,两块必需横着加进去,如果是竖着加,就相当于第一种类型排法了,重复了。
- 涉及缺更多积木时,会发现无论怎么排,排法都会和上述情况重复,也就是说,以上三种情况涵盖了a[i][0]的所有排法。
所以有:
a[i][0]=(a[i-2][0]+a[i-2][1]+a[i-1][0])%mod;
a[i][1]的排法,大家可以自行画图写出来。
写出初始状态:
a[1][0]=1,a[2][0]=2,a[1][1]=2,a[2][1]=4;
给出完整代码:
#include<stdio.h>
#define mod 1000000007
long int a[10000001][2];
int main()
{
int n;
a[1][0]=1,a[2][0]=2,a[1][1]=2,a[2][1]=4;
scanf("%d",&n);
if(n==1)printf("1");
else if(n==2)printf("2");
else {
for(int i=3;i<=n;i++){
a[i][0]=(a[i-2][0]+a[i-2][1]+a[i-1][0])%mod;
a[i][1]=(a[i-1][0]*2+a[i-1][1])%mod;
}
printf("%ld",a[n][0]%mod);
}
return 0;
}