记录一些初赛题目,主要是我做错了的和卡了我一会的,还有就是我认为自己可能在考场上会做错的,题目是直接在luogu上找的。
下面的部分解答包含了博主自己的yy和毒奶,无视就好
2018:
说实话18年初赛难度已经有点大了,坑也有点多。在洛谷做的时候最后一道题变量名敲错了,不然就AK了
由于是笔试,没有编译器来check差评
7.在一条长度为 1 的线段上随机取两个点,则以这两个点为端点的线段的期望长度是( )。
A. 1 / 2
B. 1 / 3
C. 2 / 3
D. 3 / 5
正确答案: B
听说去年很多人这道题都是瞎蒙蒙对了答案的。然而我是因为知道这个结论。
为什么初赛会考微积分 实际上考虑两个端点的位置,就是求
∫
0
1
∫
0
1
a
b
s
(
x
−
y
)
d
y
d
x
=
2
⋅
∫
0
1
∫
0
x
(
x
−
y
)
d
y
d
x
=
2
⋅
∫
0
1
x
2
2
d
x
=
1
3
\int_{0}^{1}\int_{0}^1abs(x-y)\mathrm{d}y\mathrm{d}x\\=2\cdot\int_{0}^{1}\int_{0}^x(x-y)\mathrm{d}y\mathrm{d}x\\=2\cdot\int_{0}^{1}\frac{x^2}{2}\mathrm{d}x=\frac{1}{3}
∫01∫01abs(x−y)dydx=2⋅∫01∫0x(x−y)dydx=2⋅∫012x2dx=31
求积分即可。除此以外不知道还有什么对的做法,连续过程的期望计算显然只能用微积分。
17.方程 a*b = (a or b) * (a and b),在 a, b 都取 [0, 31] 中的整数时,共有_____组解。(*表示乘法;or 表示按位或运算;and 表示按位与运算)
正确答案: 454
容易注意到一个东西 a & b ≤ min ( a , b ) ≤ max ( a , b ) ≤ a ∣ b < 2 ⋅ max ( a , b ) a\&b\leq \min(a,b)\leq \max(a,b)\leq a|b < 2\cdot \max(a,b) a&b≤min(a,b)≤max(a,b)≤a∣b<2⋅max(a,b)。
根据这个结论,稍微分类讨论一下发现上面的方程成立当且仅当
{
a
&
b
=
min
(
a
,
b
)
a
∣
b
=
max
(
a
,
b
)
\left\{\begin{aligned}a\&b=\min(a,b)\\a|b=\max(a,b)\end{aligned}\right.
{a&b=min(a,b)a∣b=max(a,b),换句话说,某一个数二进制表示应当是另一个的子集。
则答案为
2
×
(
∑
i
=
0
5
(
5
i
)
×
2
i
)
−
32
=
454
2×(\sum\limits_{i=0}^5{5\choose i}×2^i)−32=454
2×(i=0∑5(i5)×2i)−32=454
- 阅读程序写结果:
#include <iostream>
using namespace std;
const int N = 110;
bool isUse[N];
int n, t;
int a[N], b[N];
bool isSmall() {
for (int i = 1; i <= n; ++i)
if (a[i] != b[i]) return a[i] < b[i];
return false;
}
bool getPermutation(int pos) {
if (pos > n) {
return isSmall();
}
for (int i = 1; i <= n; ++i) {
if (!isUse[i]) {
b[pos] = i; isUse[i] = true;
if (getPermutation(pos + 1)) {
return true;
}
isUse[i] = false;
}
}
return false;
}
void getNext() {
for (int i = 1; i <= n; ++i) {
isUse[i] = false;
}
getPermutation(1);
for (int i = 1; i <= n; ++i) {
a[i] = b[i];
}
}
int main() {
scanf("%d%d", &n, &t);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
for (int i = 1; i <= t; ++i) {
getNext();
}
for (int i = 1; i <= n; ++i) {
printf("%d", a[i]);
if (i == n) putchar(’\n’); else putchar(’ ');
}
return 0;
}
输入1:6 10 1 6 4 5 3 2
正确答案: 2 1 3 5 6 4
输入2:6 200 1 5 3 4 2 6
正确答案: 3 2 5 6 1 4
说实话真的没有想到CCF的代码效率这么低下,连剪枝都没有。。。
实际上就是给你一个长度为
n
n
n的排列然后让你算出
t
t
t个之后的排列。
直接康托展开然后逆展开就行了。去年不会康托展开第二个问题算错了
23.完善程序
一只小猪要买 N 件物品(N 不超过 1000)。
它要买的所有物品在两家商店里都有卖。第 i 件物品在第一家商店的价格是 a[i] ,在第二家商店的价格是 b[i] ,两个价格都不小于 0 且不超过 10000。如果在第一家商店买的物品的总额不少于 50000,那么在第一家店买的物品都可以打 95 折(价格变为原来的 0.95 倍)。
求小猪买齐所有物品所需最少的总额。
输入:第一行一个数 N。接下来N 行,每行两个数。第 i 行的两个数分别代表 a[i],b[i]。
输出:输出一行一个数,表示最少需要的总额,保留两位小数。
试补全程序。
#include <cstdio>
#include <cstdlib>
using namespace std;
const int Inf = 1000000000;
const int threshold = 50000;
const int maxn = 1000;
int n, a[maxn], b[maxn];
bool put_a[maxn];
int total_a, total_b;
double ans;
int f[threshold];
int main() {
scanf("%d", &n);
total_a = total_b = 0;
for (int i = 0; i < n; ++i) {
scanf("%d%d", a + i, b + i);
if (a[i] <= b[i]) total_a += a[i];
else total_b += b[i];
}
ans = total_a + total_b;
total_a = total_b = 0;
for (int i = 0; i < n; ++i) {
if (____(1)____) {//a[i] * 0.95 <= b[i]
put_a[i] = true;
total_a += a[i];
} else {
put_a[i] = false;
total_b += b[i];
}
}
if (____(2)____) {//total_a >= threshold
printf("%.2f", total_a * 0.95 + total_b);
return 0;
}
f[0] = 0;
for (int i = 1; i < threshold; ++i)
f[i] = Inf;
int total_b_prefix = 0;
for (int i = 0; i < n; ++i)
if (!put_a[i]) {
total_b_prefix += b[i];
for (int j = threshold - 1; j >= 0; --j) {
if (____(3)____>= threshold && f[j] != Inf)//total_a + j + a[i]
ans = min(ans, (total_a + j + a[i]) * 0.95 +____(4)____);
//f[j] + total_b - total_b_prefix
f[j] = min(f[j] + b[i], j >= a[i] ?____(5)____: Inf);
//f[j - a[i]]
}
}
printf("%.2f", ans);
return 0;
}
答案见代码注释
实际上是一个很奇怪的DP+贪心。
首先可以看出第二个空的答案,同时我们知道这时要求能放到 a a a的都要尽量放到 a a a里面。于是可以看出第一个空的比较要乘上 0.95 0.95 0.95。
看到第三空,我们根据后面比较的是threshold
可以看出这里应该放的是
a
a
a里面买的东西的价格,下面乘上
0.95
0.95
0.95的显然就是了,直接抄上来即可。由于这里更新了答案,我们可以推断出第四空要填的是这个情况下
b
b
b里面买的东西的最小价格,但是目前还不知道
f
f
f维护了个什么所以填不出来。
看到第五空,根据min
的第一个参数可以知道
f
[
j
]
f[j]
f[j]求的是
a
a
a里面放了价格为
j
j
j的物品的时候,
b
b
b里面最少要放多少。那么直接得到第五空的答案,然后回去把第四空填好就行了。