试题 A: 阶乘求和
本题总分:5 分
【问题描述】
令 S = 1! + 2! + 3! + ... + 202320232023!,求 S 的末尾 9 位数字。
提示:答案首位不为 0。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一
个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
找规律,可以先手动模拟几次,会发现 随着n越大,零也越多,当n为40的时候刚好有9个0
所以到40项以后的末尾9个阶乘的和一定是不变的,可以用手算,也可以写程序
答案是,901327897
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=110000;
int f[N];
signed main()
{
ios::sync_with_stdio(false);
f[1]=1;
for(int i=2;i<=50;i++)
f[i]=f[i-1]*i;
for(int i=2;i<=50;i++)
f[i]=f[i-1]+f[i];
cout<<f[40]%1000000000;
return 0;
}
试题 B: 幸运数字
本题总分:5 分
【问题描述】
哈沙德数是指在某个固定的进位制当中,可以被各位数字之和整除的正整
数。例如 126 是十进制下的一个哈沙德数,因为 (126)10 mod (1+2+6) = 0;126
也是八进制下的哈沙德数,因为 (126)10 = (176)8,(126)10 mod (1 + 7 + 6) = 0;
同时 126 也是 16 进制下的哈沙德数,因为 (126)10 = (7e)16,(126)10 mod (7 +
e) = 0。小蓝认为,如果一个整数在二进制、八进制、十进制、十六进制下均为
哈沙德数,那么这个数字就是幸运数字,第 1 至第 10 个幸运数字的十进制表示
为:1 , 2 , 4 , 6 , 8 , 40 , 48 , 72 , 120 , 126 . . . 。现在他想知道第 2023 个幸运数
字是多少?你只需要告诉小蓝这个整数的十进制表示即可。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一
个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
Java中有十进制转化为二进制,十六进制,八进制的方法,暴力枚举一下即可。(因为不会Java所以懒得写了)
试题 C: 数组分割
时间限制: 1.0s 内存限制: 512.0MB 本题总分:10 分
【问题描述】
小蓝有一个长度为 N 的数组 A = [A0, A1, . . . , AN−1]。现在小蓝想要从 A 对
应的数组下标所构成的集合 I = {0, 1, 2, . . . , N − 1} 中找出一个子集 R1,那么 R1
在 I 中的补集为 R2。记 S 1 =
∑
r∈R1
Ar,S 2 =
∑
r∈R2
Ar,我们要求 S 1 和 S 2 均为
偶数,请问在这种情况下共有多少种不同的 R1。当 R1 或 R2 为空集时我们将
S 1 或 S 2 视为 0。
【输入格式】
第一行一个整数 T,表示有 T 组数据。
接下来输入 T 组数据,每组数据包含两行:第一行一个整数 N,表示数组
A 的长度;第二行输入 N 个整数从左至右依次为 A0, A1, . . . , AN−1,相邻元素之
间用空格分隔。
【输出格式】
对于每组数据,输出一行,包含一个整数表示答案,答案可能会很大,你
需要将答案对 1000000007 进行取模后输出。
【样例输入】
2
2
6 6
2
1 6
【样例输出】
4
试题C: 数组分割 4
第十四届蓝桥杯大赛软件赛省赛 Java 大学 B 组
0
【样例说明】
对于第一组数据,答案为 4。(注意:大括号内的数字表示元素在数组中的
下标。)
R1 = {0}, R2 = {1};此时 S 1 = A0 = 6 为偶数, S 2 = A1 = 6 为偶数。
R1 = {1}, R2 = {0};此时 S 1 = A1 = 6 为偶数, S 2 = A0 = 6 为偶数。
R1 = {0, 1}, R2 = {};此时 S 1 = A0 + A1 = 12 为偶数, S 2 = 0 为偶数。
R1 = {}, R2 = {0, 1};此时 S 1 = 0 为偶数, S 2 = A0 + A1 = 12 为偶数。
对于第二组数据,无论怎么选择,都不满足条件,所以答案为 0。
【评测用例规模与约定】
对于 20% 的评测用例,1 ≤ N ≤ 10。
对于 40% 的评测用例,1 ≤ N ≤ 102。
对于 100% 的评测用例,1 ≤ T ≤ 10, 1 ≤ N ≤ 103
, 0 ≤ Ai ≤ 109。
做法:线性dp
先看思路,当数组总和为奇数的时候一定不存在R1和R2,因为R2是R1的补集,当总和为奇数的时候,R1和R2的奇偶性一定不同,因为奇数加偶数才是奇数。
这样我们就可以定义状态
令表示的是当前是第个元素,并且奇偶性是
其中表示的是当前第i个元素为偶数,当前第i个元素是奇数
答案就是
状态转移方程:
如果是偶数那么就有
因为偶数加偶数还是偶数
奇数加奇数还是偶数
如果是奇数那么就有
因为奇数加偶数还是奇数下面同理
即可求解出答案,以下是代码
#include<bits/stdc++.h>
#define int long long
#define x first
#define y second
using namespace std;
const int N=21000,mod=1000000007;
int n,a[N],s[N];
int f[N][3];
void solve()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
memset(f,0,sizeof f);
int sum=0;
for(int i=1;i<=n;i++)
sum+=a[i];
if(sum%2)
{
cout<<0<<endl;
return;
}
int ans=1;
for(int i=1;i<=n;i++)
{
if(a[i]%2==0)
{
f[i][0]=1;
for(int j=1;j<i;j++)
{
f[i][0]=(f[i][0]+f[j][0])%mod;
f[i][1]=(f[i][1]+f[j][1])%mod;
}
}
else
{
f[i][1]=1;
for(int j=1;j<i;j++)
{
f[i][1]=(f[i][1]+f[j][0])%mod;
f[i][0]=(f[i][0]+f[j][1])%mod;
}
}
ans=(f[i][0]+ans)%mod;
}
cout<<ans<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
int t;
cin>>t;
while(t--)
solve();
return 0;
}
试题 D: 矩形总面积
时间限制: 1.0s 内存限制: 512.0MB 本题总分:10 分
【问题描述】
平面上有个两个矩形 R1 和 R2,它们各边都与坐标轴平行。设 (x1, y1) 和
(x2, y2) 依次是 R1 的左下角和右上角坐标,(x3, y3) 和 (x4, y4) 依次是 R2 的左下
角和右上角坐标,请你计算 R1 和 R2 的总面积是多少?
注意:如果 R1 和 R2 有重叠区域,重叠区域的面积只计算一次。
【输入格式】
输入只有一行,包含 8 个整数,依次是:x1,y1,x2,y2,x3,y3,x4 和 y4。
【输出格式】
一个整数,代表答案。
【样例输入】
2 1 7 4 5 3 8 6
【样例输出】
22
【样例说明】
样例中的两个矩形如图所示:
试题 D: 矩形总面积 6
第十四届蓝桥杯大赛软件赛省赛 Java 大学 B 组
【评测用例规模与约定】
对于 20% 的数据,R1 和 R2 没有重叠区域。
对于 20% 的数据,其中一个矩形完全在另一个矩形内部。
对于 50% 的数据,所有坐标的取值范围是 [0, 103
]。
对于 100% 的数据,所有坐标的取值范围是 [0, 105
]。
思路一:扫描线模板
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 100010;
struct segment
{
int x, y1,y2;
int d;
bool operator < (const segment&t)const
{
return x < t.x;
}
}seg[N * 2];
struct node
{
int l,r;
int cnt;
int len;
}tr[N * 8];
vector<int>ys;
int n;
int find(int y)
{
return lower_bound(ys.begin(), ys.end(), y) - ys.begin();
}
void pushup(int u)
{
if(tr[u].cnt)tr[u].len = ys[tr[u].r + 1] - ys[tr[u].l];
else if(tr[u].l != tr[u].r)
{
tr[u].len = tr[u << 1].len + tr[u << 1 | 1].len;
}
else tr[u].len = 0;
}
void modify(int u,int l,int r,int d)
{
if(tr[u].l >= l && tr[u].r <= r)
{
tr[u].cnt += d;
pushup(u);
}
else
{
int mid = tr[u].r + tr[u].l >> 1;
if (l <= mid)modify(u << 1,l,r,d);
if (r > mid)modify(u << 1 | 1,l,r,d);
pushup(u);
}
}
void build(int u,int l,int r)
{
tr[u] = {l,r,0,0};
if (l != r)
{
int mid = l + r >> 1;
build(u << 1,l,mid),build(u << 1 | 1,mid + 1,r);
}
}
signed main()
{
int T = 1;
n=2;
while (n)
{
ys.clear();
int j = 0;
for (int i = 0 ; i < n ; i ++)
{
int x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
seg[j ++] = {x1,y1,y2,1};
seg[j ++] = {x2,y1,y2,-1};
ys.push_back(y1),ys.push_back(y2);
}
sort(seg,seg + j);
sort(ys.begin(),ys.end());
ys.erase(unique(ys.begin(),ys.end()),ys.end());
build(1,0,ys.size() - 2);
int res = 0;
for (int i = 0 ; i < j ; i ++)
{
if (i)res += tr[1].len * (seg[i].x - seg[i - 1].x);
modify(1,find(seg[i].y1), find(seg[i].y2) - 1,seg[i].d);
}
cout<<res<<endl;
break;
}
return 0;
}
思路二:用数学
如果两个矩形相交的话就将一个两个矩形补全成一个大矩形,然后减去两个多出来的小矩形即可。
代码就懒得写了
试题 E: 蜗牛
时间限制: 1.0s 内存限制: 512.0MB 本题总分:15 分
【问题描述】
这天,一只蜗牛来到了二维坐标系的原点。
在 x 轴上长有 n 根竹竿。它们平行于 y 轴,底部纵坐标为 0,横坐标分别
为 x1, x2, ..., xn。竹竿的高度均为无限高,宽度可忽略。蜗牛想要从原点走到第
n 个竹竿的底部也就是坐标 (xn, 0)。它只能在 x 轴上或者竹竿上爬行,在 x 轴
上爬行速度为 1 单位每秒;由于受到引力影响,蜗牛在竹竿上向上和向下爬行
的速度分别为 0.7 单位每秒和 1.3 单位每秒。
为了快速到达目的地,它施展了魔法,在第 i 和 i + 1 根竹竿之间建立了传
送门(0 < i < n),如果蜗牛位于第 i 根竹竿的高度为 ai 的位置 (xi
, ai),就可以
瞬间到达第 i + 1 根竹竿的高度为 bi+1 的位置 (xi+1, bi+1),请计算蜗牛最少需要
多少秒才能到达目的地。
【输入格式】
输入共 1 + n 行,第一行为一个正整数 n;
第二行为 n 个正整数 x1, x2, . . . , xn;
后面 n − 1 行,每行两个正整数 ai
, bi+1。
【输出格式】
输出共一行,一个浮点数表示答案(四舍五入保留两位小数)。
【样例输入】
3
1 10 11
1 1
2 1
试题E: 蜗牛 8
第十四届蓝桥杯大赛软件赛省赛 Java 大学 B 组
【样例输出】
4.20
【样例说明】
蜗牛路线:
(0, 0) → (1, 0) → (1, 1) → (10, 1) → (10, 0) → (11, 0),花费时间为 1 + 0
1
.7 +
0 + 1
1
.3 + 1 ≈ 4.20
【评测用例规模与约定】
对于 20% 的数据,保证 n ≤ 15;
对于 100% 的数据,保证 n ≤ 105,ai
, bi ≤ 104,xi ≤ 109。
思路:线性dp
定义状态表示的是当前所在的杆子是第个杆子表示的是,是否使用传送门
表示用传送门,表示不用传送门
状态转移方程就可以写成
当使用传送门的时候可以写成
其中的意思表示的是
如果此时传送过来的位置高于在出传送点的位置的时候就要滑下去此时速度是
反之就要爬上去速度是
答案就是
代码
#include<bits/stdc++.h>
#define int long long
#define x first
#define y second
using namespace std;
const int N=110000;
int n;
double x[N],a[N],b[N];
double f[N][3];//f[i][1]传送门,f[i][0]
signed main() {
ios::sync_with_stdio(false);
cin >> n;
for (int i = 1; i <= n; i++)
cin >> x[i];
for (int i = 1; i < n; i++)
cin >> a[i] >> b[i];
f[1][0] = x[1];
f[1][1] = x[1] + a[1] / 0.7;
for (int i = 2; i <= n; i++)
{
f[i][0]=min(f[i-1][0]+x[i]-x[i-1],f[i-1][1]+b[i-1]/1.3);
f[i][1]=min(f[i][0]+a[i]/0.7,f[i-1][1]+((b[i-1]>a[i])?(b[i-1]-a[i])/1.3:(a[i]-b[i-1])/0.7));
}
printf("%.2lf",f[n][0]);
return 0;
}
试题 G: 买二赠一
时间限制: 1.0s 内存限制: 512.0MB 本题总分:20 分
【问题描述】
某商场有 N 件商品,其中第 i 件的价格是 Ai。现在该商场正在进行 “买二
赠一” 的优惠活动,具体规则是:
每购买 2 件商品,假设其中较便宜的价格是 P(如果两件商品价格一样,
则 P 等于其中一件商品的价格),就可以从剩余商品中任选一件价格不超过 P
2
的商品,免费获得这一件商品。可以通过反复购买 2 件商品来获得多件免费商
品,但是每件商品只能被购买或免费获得一次。
小明想知道如果要拿下所有商品(包含购买和免费获得),至少要花费多少
钱?
【输入格式】
第一行包含一个整数 N。
第二行包含 N 个整数,代表 A1, A2, A3, . . . ,AN。
【输出格式】
输出一个整数,代表答案。
【样例输入】
7
1 4 2 8 5 7 1
【样例输出】
25
试题 G: 买二赠一 13
第十四届蓝桥杯大赛软件赛省赛 Java 大学 B 组
【样例说明】
小明可以先购买价格 4 和 8 的商品,免费获得一件价格为 1 的商品;再后
买价格为 5 和 7 的商品,免费获得价格为 2 的商品;最后单独购买剩下的一件
价格为 1 的商品。总计花费 4 + 8 + 5 + 7 + 1 = 25。不存在花费更低的方案。
【评测用例规模与约定】
对于 30% 的数据,1 ≤ N ≤ 20。
对于 100% 的数据,1 ≤ N ≤ 5 × 105,1 ≤ Ai ≤ 109。
思路:简单的贪心,每次取最大的两个然后免费其中两个中最小的/2就行
代码:
#include<bits/stdc++.h>
#define int long long
#define x first
#define y second
using namespace std;
const int N=510000;
int n;
int a[N];
int check()
{
priority_queue<int>st;
int res=0;
int t=0;
for(int i=n;i;i--)
{
if(!st.empty()&&st.top()>=a[i])
{
st.pop();
continue;
}
res+=a[i];
t++;
if(t>=2){
t=0;
st.push(a[i]/2);
}
}
return res;
}
signed main()
{
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
int l=-1,r=5e12+1;
sort(a+1,a+1+n);
cout<<check()<<endl;
return 0;
}
试题 J: 魔法阵
时间限制: 1.0s 内存限制: 512.0MB 本题总分:25 分
【问题描述】
魔法师小蓝为了营救自己的朋友小 Q,来到了敌人布置的魔法阵。魔法阵
可以看作是一幅具有 N 个结点 M 条边的无向图,结点编号为 0, 1, 2, . . . , N − 1,
图中没有重边和自环。敌人在每条边上都布置了陷阱,每条边都有一个伤害属
性 w,每当小蓝经过一条边时就会受到这条边对应的 w 的伤害。小蓝从结点 0
出发,沿着边行走,想要到达结点 N − 1 营救小 Q。
小蓝有一种特殊的魔法可以使用,假设一条路径按照顺序依次经过了以下
L 条边:e1, e2, . . . , eL(可以出现重复的边),那么期间小蓝受到的总伤害就是
P =
∑L
i=1 w(ei),w(ei) 表示边 ei 的伤害属性。如果 L ≥ K,那么小蓝就可以从
这 L 条边当中选出连续出现的 K 条边 ec
, ec+1, . . . , ec+K−1 并免去在这 K 条边行
走期间所受到的伤害,即使用魔法之后路径总伤害变为 P
′ = P −
∑c+K−1
i=c w(ei)。
注意必须恰好选出连续出现的 K 条边,所以当 L < K 时无法使用魔法。
小蓝最多只可以使用一次上述的魔法,请问从结点 0 出发到结点 N − 1 受
到的最小伤害是多少?题目保证至少存在一条从结点 0 到 N − 1 的路径。
【输入格式】
第一行输入三个整数,N, K, M,用空格分隔。
接下来 M 行,每行包含三个整数 u, v,w,表示结点 u 与结点 v 之间存在一
条伤害属性为 w 的无向边。
【输出格式】
输出一行,包含一个整数,表示小蓝从结点 0 到结点 N − 1 受到的最小伤
害。
【样例输入 1】
4 2 3
试题J: 魔法阵 20
第十四届蓝桥杯大赛软件赛省赛 Java 大学 B 组
0 1 2
1 2 1
2 3 4
【样例输出 1】
2
【样例输入 2】
2 5 1
0 1 1
【样例输出 2】
0
【样例说明】
样例 1,存在路径:0 → 1 → 2 → 3,K = 2,如果在 0 → 1 → 2 上使用魔
法,那么答案就是 0 + 0 + 4 = 4;如果在 1 → 2 → 3 上使用魔法,那么答案就
是 2 + 0 + 0 = 2。再也找不到比 2 还小的答案了,所以答案就是 2。
样例 2,存在路径:0 → 1 → 0 → 1 → 0 → 1,K = 5,这条路径总计恰好
走了 5 条边,所以正好可以用魔法消除所有伤害,答案是 0。
【评测用例规模与约定】
对于 30% 的评测用例,1 ≤ N ≤ 20。
对于 50% 的评测用例,1 ≤ N ≤ 100。
对于 100% 的评测用例,1 ≤ N ≤ 1000, 1 ≤ M ≤
N×(N−1)
2 ,1 ≤ K ≤ 10,
0 ≤ u, v ≤ N − 1,1 ≤ w ≤ 1000
思路:最短路+线性dp
压轴题难度并不大很经典的题目,以往最短路中表示第个点中最短的距离
这里就需要稍微转化一下表示的是第个点中免费了条边的费用的最短距离。
所以有三种更新方法
假设此时第个点的邻接点是
第一种更新方法就是当的时候就是正常最短路
也即是
第二种更新方式
免费了条边的最小费用
也就是
第三种免费完k条边后的最小费用
结果即使
代码
#include<bits/stdc++.h>
using namespace std;
const int N=2100000,M=2100;
int h[N],ne[N],e[N],w[N],idx;
int f[M][M];
bool st[N];
int n,m,k;
void add(int a,int b,int c)
{
w[idx]=c,e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void spfa()
{
memset(f,0x3f,sizeof f);
queue<int>q;
q.push(0);
f[0][0]=0;
while(q.size())
{
int t=q.front();
q.pop();
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
bool flag=false;
if(f[j][0]>f[t][0]+w[i])
{
f[j][0]=f[t][0]+w[i];
flag=true;
}
for(int l=1;l<=k;l++)
{
if(f[j][l]>f[t][l-1])
{
f[j][l]=f[t][l-1];
flag=true;
}
}
if(f[j][k]>f[t][k]+w[i])
{
f[j][k]=f[t][k]+w[i];
flag=true;
}
if(flag)q.push(j);
}
}
}
int main()
{
ios::sync_with_stdio(false);
memset(h,-1,sizeof h);
cin>>n>>k>>m;
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c),add(b,a,c);
}
spfa();
cout<<min(f[n-1][0],f[n-1][k]);
return 0;
}