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大佬。

posted @ 2020-10-01 21:44  C锥  阅读(171)  评论(0编辑  收藏  举报