1001考试
1001考试总结
T1
题目大意:
石子一共有n堆,其中第i堆恰好有i粒石子。小林先取,亮亮后取,并且两人依次轮流取石。每一次取石子的人可以选择任意一堆还未被取完的石子,并取走这一堆中任意多粒石子(注意,不能一粒石子也不取,也不能同时在多堆石子中取石)。最终,无石可取的人为败。小林和亮亮都十分聪明,他们的每次取石都会采取最优策略。在经过多次游戏后,小林发现了先手必胜的条件,但他不满足于此,他想知道,在知道石子的堆数n后,他第一次取石有多少种方式可以获胜。
博弈论早忘了,不过正解好像是找规律233333
首先我们得知道一个规律:
S表示1~n的异或和:
n % 4 == 0, s = n;
n % 4 == 1, s = 1;
n % 4 == 2, s = n + 1;
n % 4 == 3, s = 0;
最简单的证明:打标找规律。不过网上还是有证明的,我不会。
当\(n \% 4 == 3\)时,输出0,因为先手必输。最简单的nim游戏。
当\(n \% 4 == 1\)时,我们对任意一个奇数堆进行操作都可以使异或和变为0,答案为\((n + 1) / 2\)。
剩下那两种情况,S与n位数相同。设它们的最高位为t,S的第t位为1,为使S变为0,必须修改一个第t位为1的数。显然修改任一第t位为1的数都可以使S变为0。答案即为n去掉最高位再加1。(题解原话)
这是90分代码,最后10分打高精,不想写了。
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 5005;
int T;
long long n;
int SG[N];
int main() {
freopen("nim.in","r",stdin); freopen("nim.out","w",stdout);
T = read();
while(T --> 0) {
n = read();
if(n % 4 == 3) printf("0\n");
if(n % 4 == 1) printf("%lld\n", (n + 1) / 2);
if(n % 4 == 2 || n % 4 == 0) {
long long y = 1, x = n;
while(y <= x) y *= 2;
y /= 2; x -= y; x++;
printf("%lld\n", x);
}
}
fclose(stdin); fclose(stdout);
return 0;
}
T2
题目大意:
小林和亮亮是好朋友。小林有一个幸运数字a,亮亮有一个幸运数字b。一个数字称之为“幸运数”当且仅当它只由a和b组成。小林称一个数为“魔数”,当且仅当这个数各数位之和为“幸运数”,且这个数字本身也是“幸运数”。举个例子:小林的幸运数字为2,亮亮的幸运数字为6,那么23不是“幸运数”,而26、222是“幸运数”。进一步,222是“魔数”(因为2+2+2=6),而26不是“魔数”(因为2+6=8)。亮亮想要知道,有多少个n位的“魔数”(一个n位数不包含前导0),由于这个数字会很大,所以亮亮只关心这个数模1000000007(10^9+7,是一个质数)的结果。
这个题还挺简单的,机房好多人都切了。
直接枚举a的个数,那么b的个数也相应的出来了,然后判断这个数的和是否是幸运数,如果是的话给答案加上\(C_{n}^{i}\)。
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 1e6 + 5, mod = 1e9 + 7;
int a, b, n, ans;
int fac[N], inv[N];
int calc(long long x) {
while(x) {
int tmp = x % 10; x /= 10;
if(!(tmp == a || tmp == b)) return 0;
}
return 1;
}
void make_pre() {
fac[0] = fac[1] = inv[0] = inv[1] = 1;
for(int i = 2;i <= N - 5; i++) fac[i] = 1ll * fac[i - 1] * i % mod;
for(int i = 2;i <= N - 5; i++) inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;
for(int i = 2;i <= N - 5; i++) inv[i] = 1ll * inv[i - 1] * inv[i] % mod;
}
int C(int x, int y) {
if(x < y) return 0;
return 1ll * fac[x] * inv[y] % mod * inv[x - y] % mod;
}
int main() {
freopen("number.in","r",stdin); freopen("number.out","w",stdout);
a = read(); b = read(); n = read(); make_pre();
for(int i = 0;i <= n; i++)
if(calc(1ll * a * i + 1ll * b * (n - i))) ans = (ans + C(n, i)) % mod;
printf("%d", ans);
fclose(stdin); fclose(stdout);
return 0;
}
T3
题目大意:
小林和亮亮刚入高中,首先就迎来了军训。这一天,他们班的同学正在教官的指导下进行队列练习。班上总共有n位同学,他们站成一排,然而,本该站成一条直线的他们却排成了“一字长蛇阵”。用数学的语言来描述,如果把训练场看成一个平面直角坐标系,第i名同学所在位置的横坐标是i,而所有同学的纵坐标本该是0(或者任意一个相等的常量),这样就排成了一条直线。当然,由于同学们排的歪歪扭扭,所以第i位同学的横坐标依然是i,而纵坐标却成了iY(为了简化问题,我们假设所有的坐标都是整数)。对此,教官当然十分生气,因此他决定下命令调整队伍,使得所有人能够站成一条直线(也即让所有的iY相同)。教官的命令总共有三种:1、除了某一个同学外,其余所有同学向前走一步(向前走一步可理解为iY的值加1,下同);2、除了某一个同学外,其余所有同学向前走两步;3、除了某一个同学外,其余所有同学向前走五步。教官希望他能以最少的命令次数使得所有同学站成一条直线,但是他不会算,于是就让亮亮帮他来计算。亮亮虽然聪明,可是由于班上同学人数众多,他一下子也解决不了这个问题,只能来寻求会编程的你的帮助,你能告诉亮亮答案吗?
乱搞吧。
我们先按高度排个序,现在我们枚举到了\(a[i]\),想让\(a[i - 1]\)与它的高度相同,我们搞一个指令:让处理\(i\)这个位置其他位置都走\(y\)步,假设与\(a[i]\)等高的有\(x\)个,那么所有\(a[i]\)高度的应该是往前走了\((x - 1) *y\)步,所有\(a[i - 1]\)高度的应该是走了\(x * y\)步。
现在我们的到来一个等式:\(a[i] +(x - 1) * y = a[i - 1] + x * y\),化简一下:\(y = a[i] - a[i - 1]\)。这样我们就可以知道应该走多少步可以使高度为\(a[i], a[i - 1]\)相同了。注意记录一个\(last\),表示之前就已经加过多少高度了。
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 1e5 + 5;
int n, cnt, a[N];
long long ans, last;
struct que { int x, h; } b[N];
long long calc(long long x) {
long long res = 0;
long long x1 = x / 5, x2 = x % 5;
res += x1;
if(x2 != 0) {
x = x % 5;
x1 = x / 2; x2 = x % 2;
res += x1 + x2;
}
return res;
}
int main() {
freopen("queue.in","r",stdin); freopen("queue.out","w",stdout);
n = read();
for(int i = 1;i <= n; i++) a[i] = read();
sort(a + 1, a + n + 1);
a[0] = -1; //一定注意这里,不然有0的情况会错!!!
for(int i = 1;i <= n; i++)
if(a[i] != a[i - 1]) b[++cnt].h = a[i], b[cnt].x = 1;
else b[cnt].x ++;
for(int i = 2;i <= cnt; i++) {
long long der = b[i].h - b[i - 1].h + last;
ans += 1ll * b[i].x * calc(der); last = der;
}
printf("%lld", ans);
fclose(stdin); fclose(stdout);
return 0;
}
/*
4
1 1 2 6
2
6
1 1 2 2 6 8
5
8
1 3 3 5 5 5 7 7
12
8
1 2 4 6 2 5 4 5
11
9
5 6 1 5 5 2 2 1 5
11
8
9 6 8 9 6 8 5 4
9
*/
总结
期望0 + 100 +100 = 200pts,实得0 +100 + 90 = 190pts。预期挺准的吧。
其实第一题那种题可以打个表找找规律的,别懒得打。
多考虑考虑细节,第三题那10分不该丢。
%%%机房里290大佬。