题解(HAUT2019级新生周赛(六)-张伟涛专场)

题解(HAUT2019级新生周赛(六)-张伟涛专场)

问题 A: 棋盘上的米粒

模拟签到题

从第一个格子开始放米粒,如果米粒数量为正数,就代表当前位置的格子是可以放上米粒的,放不放满无所谓,维护一个 k k k 代表放到第几个格子。

C++版代码
#include <iostream>
using namespace std;
typedef long long LL;

LL n;

int sol() {
	int k = 0;
	LL tem = 1;
	while (n > 0) {
		k++;
		n -= tem;
		tem *= 2;
	}
	return k;
}

int main() {
	while (cin >> n) {
		cout << sol() << "\n";
	}
	return 0;
}
C语言版代码
#include <stdio.h>
typedef long long LL;
int main() {
	LL n, tem, k;
	while (scanf("%lld", &n) != EOF) {
		tem = 1; k = 0;
		while (n > 0) {
			n -= tem;
			tem *= 2;
			k++;
		}
		printf("%lld\n", k);
	}
	return 0;
}

问题 B: 不一样的蛋糕

二分基础题

二分切出的蛋糕的大小,然后检验大小是否合格,不断夹逼最优值即可

C++版代码
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
typedef long long LL;

int n, m;
vector<LL> a;
LL sum;

void init() {
	sum = 0;
	a.resize(n);
}

bool che(double mid) {
	int num = 0;
	for (int i = 0; i < n; ++i) {
		num += (int)(a[i] / mid);
	}
	return num >= m;
}

double sol() {
	init();
	for (int i = 0; i < n; ++i) cin >> a[i], sum += a[i];
	double l = 0, r = sum, ans = r;
	while (r - l > 1e-5) {
		double mid = (l + r) / 2;
		if (che(mid)) ans = mid, l = mid;
		else r = mid;
	}
	return ans;
}

int main() {
	while(cin >> n >> m) {
		printf("%.2f\n", sol());
	}
	return 0;
}
C语言版代码
#include <stdio.h>
#define maxn 10005
#define LL long long

int n, m;
LL a[maxn], al;
LL sum;

void init() {
	sum = 0;
	al = 0;
}

int che(double mid) {
	int num = 0;
	for (int i = 0; i < n; ++i) {
		num += (int)(a[i] / mid);
	}
	return num >= m;
}

double sol() {
	init();
	for (int i = 0; i < n; ++i) scanf("%lld", &a[i]), sum += a[i];
	double l = 0, r = sum, ans = r;
	while (r - l > 1e-5) {
		double mid = (l + r) / 2;
		if (che(mid)) ans = mid, l = mid;
		else r = mid;
	}
	return ans;
}

int main() {
	while (~scanf("%d%d", &n, &m)) {
		printf("%.2f\n", sol());
	}
	return 0;
}

问题 C: HJ病毒

快速幂基础题

由1+1=2我们可以知道: 答案是 n × 2 t n×2^t n×2t

然后用快速幂算出 2 t 2^t 2t 即可,不要忘记取模

C++版代码
#include <iostream>
#define mod 1000000007
using namespace std;
typedef long long LL;

LL n, t;

LL q_w(LL x, LL t) {
	LL ans = 1;
	while (t) {
		if (t & 1) ans *= x, ans %= mod;
		x *= x;
		x %= mod;
		t >>= 1;
	}
	return ans;
}

int main() {
	while (cin >> n >> t) {
		cout << n * q_w(2, t) % mod << "\n";
	}
	return 0;
}
C语言版代码
#include <stdio.h>
#define mod 1000000007
typedef long long LL;

LL n, t;

LL q_w(LL x, LL t) {
	LL ans = 1;
	while (t) {
		if (t & 1) ans *= x, ans %= mod;
		x *= x;
		x %= mod;
		t >>= 1;
	}
	return ans;
}

int main() {
	while (~scanf("%lld%lld", &n, &t)) {
		printf("%lld\n", n * q_w(2, t) % mod);
	}
	return 0;
}

问题 D: 最小的整数

一个奇数如果旁边有偶数的话,它就可以和旁边的偶数换位置,偶数亦然。那么如果一个序列里有多个数字,但是只有一个是奇数,剩下的全是偶数的话,奇数就可以出现在任何它想去的位置。也就是说奇数和偶数之间的相对位置是可以随意设置的,但是奇数不能和奇数换位置,那么奇数之间的相对位置就是固定的,偶数亦然。

那么把序列里的奇数和偶数分离开,然后把两个序列按字典序最小合并到一个序列里就是答案了。

C++版代码
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#define maxn 300005
using namespace std;
const int inf = 0x3f3f3f3f;

vector<int>b[2];
string ans, a;

void init() {
	b[0].clear();
	b[1].clear();
	ans.clear();
}

int main() {
	while (cin >> a) {
		init();
		for (int i = 0; i < a.size(); i++) {
			int x = a[i] - '0';
			b[x % 2].push_back(x);
		}
		vector<int>::iterator i = b[0].begin(), j = b[1].begin();
		while (i != b[0].end() || j != b[1].end()) {
			int tem = inf, f = -1;
			if (i != b[0].end() && *i < tem) tem = *i, f = 0;
			if (j != b[1].end() && *j < tem) tem = *j, f = 1;
			ans += tem + '0';
			(f ? j : i)++;
		}
		cout << ans << "\n";
	}
	cout << 0;
	return 0;
}
C语言版代码
#include <stdio.h>
#include <string.h>
#define maxn 300005
const int inf = 0x3f3f3f3f;

int b[2][maxn], bl[2];
char ans[maxn], a[maxn];
int ansl = 0, al = 0;

void init() {
	bl[0] = bl[1] = 0;
	ansl = 0;
	al = strlen(a);
}

int main() {
	while (~scanf("%s", a)) {
		init();
		for (int i = 0; i < al; i++) {
			int x = a[i] - '0';
			b[x % 2][bl[x % 2]++] = x;
		}
		int i = 0, j = 0;
		while (i < bl[0] || j < bl[1]) {
			int tem = inf, f = -1;
			if (i < bl[0] && b[0][i] < tem) tem = b[0][i], f = 0;
			if (j < bl[1] && b[1][j] < tem) tem = b[1][j], f = 1;
			ans[ansl++] = tem + '0';
			if (f) j++;
			else i++;
		}
		ans[ansl] = '\0';
		printf("%s\n", ans);
	}
	return 0;
}

问题 E: 第k大分数(简单版)

暴力基础题

先把所有的形成的分数保存起来,然后按从大到小快排一遍,最后输出第 k k k 个即可

C++版代码
#include <iostream>
#include <algorithm>
#include <vector>
#define _for(i, a) for(int i = 0; i < (a); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
using namespace std;
typedef long long LL;

struct poi {
	LL n, d;
	poi() {}
	poi(int n, int d) :n(n), d(d) {}
	bool operator < (const poi &b) const {
		return 1.0 * n / d > 1.0 * b.n / b.d;
	}
};

LL n, k;
vector<poi> a;

void sol() {
	_for(i, n) {
		LL tem;
		cin >> tem;
		_rep(i, 1, tem - 1) a.push_back(poi(i, tem));
	}
	sort(a.begin(), a.end());
	cout << a[k - 1].n << "/" << a[k - 1].d << "\n";
}

int main() {
	while (cin >> n >> k) {
		sol();
	}
	return 0;
}
C语言版代码
#include <stdio.h>
#include <stdlib.h>
#define _for(i, a) for(int i = 0; i < (a); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define maxn 1000005
typedef long long LL;

typedef struct {
	LL n, d;
}poi;

LL n, k;
poi a[maxn];
int al = 0;

int cmp(const void*a, const void*b) {
	poi aa = *(poi*)a, bb = *(poi*)b;
	if (1.0 * aa.n / aa.d - 1.0 * bb.n / bb.d < 0) return 1;
	else if (1.0 * aa.n / aa.d - 1.0 * bb.n / bb.d > 0) return -1;
	else return 0;
}

void sol() {
	_for(i, n) {
		LL tem;
		scanf("%lld", &tem);
		_rep(i, 1, tem - 1) {
			a[al].n = i;
			a[al].d = tem;
			al++;
		}
	}
	qsort(a, al, sizeof(poi), cmp);
	printf("%lld/%lld\n", a[k - 1].n, a[k - 1].d);
}

int main() {
	while (~scanf("%lld%lld", &n, &k)) {
		sol();
	}
	return 0;
}

问题 F: 第k大分数(加强版)

二分提高题

因为给出的 p i p_i pi 是质数,所以每一个分数对应的小数都是独一无二的,就像只能有 1 2 \frac{1}{2} 21,而不会有 2 4 \frac{2}{4} 42 一样。

而且当分母一定时,分子的大小顺序就是对应的分数的大小顺序,就像 4 5 > 3 5 > 2 5 > 1 5 \frac{4}{5}>\frac{3}{5}>\frac{2}{5}>\frac{1}{5} 54>53>52>51

那么如果我们可以知道大于小数 m i d mid mid ,且分母为 P P P 的分数的个数 c n t cnt cnt 的话,我们就可以枚举 n n n 个分母求得所有大于 m i d mid mid 的分数的个数 c n t cnt cnt ,通过比较这个 c n t cnt cnt k k k 的大小就可以知道 m i d mid mid 是太大了还是太小了,二分夹逼,直到最后 c n t = k cnt = k cnt=k

那么怎么获取这个 c n t cnt cnt 呢?我们知道当分母为 5 5 5 时,构成的分数是: 4 5 , 3 5 , 2 5 和 1 5 \frac{4}{5},\frac{3}{5},\frac{2}{5}和\frac{1}{5} 54,53,5251 ,假设 m i d mid mid 此时为 0.5 0.5 0.5 ,那么把 0.5 0.5 0.5 转化为一个分数且分母为 5 5 5 的最简单的方式就是分子分母同时乘以 5 5 5 0.5 = 5   ×   0.5 5 0.5=\frac{5\ ×\ 0.5}{5} 0.5=55 × 0.5。所以得到的分数就是 2.5 5 \frac{2.5}{5} 52.5,显然,分子大于 2.5 2.5 2.5 的有 2 2 2 个,即 ( 5 − ⌊ 2.5 ⌋ −   1 5-\lfloor2.5\rfloor -\ 1 52.5 1)(符号意为向下取整);同理当分母为 P P P 时,大于 m i d mid mid 的分数的个数就是 P − ⌊ P   ⋅   m i d ⌋ − 1 P-\lfloor P\ ·\ mid \rfloor-1 PP  mid1,。

而每一次求 c n t cnt cnt 时我们都保存最接近 m i d mid mid 的分数的分子和分母,那么二分结束时的分子和分母就是要输出的答案了。

C++版代码
#include <iostream>
#include <algorithm>
#define maxn 1005
#define _for(i, a) for(LL i = 0; i < (a); ++i)
using namespace std;
typedef long long LL;

LL n, k;
LL P[maxn];
LL Numerator, Denominator;	//最接近mid的分子和分母

LL find(double mid) {
	LL cnt = 0;
	_for(i, n) {
		LL num = mid * P[i];	//向下取整
		cnt += P[i] - num - 1;	//比mid小的分数
		if (Numerator == -1 && Denominator == -1 || Numerator * P[i] > (num + 1) * Denominator) {	//保存分子和分母
			Numerator = num + 1;
			Denominator = P[i];
		}
	}
	return cnt;
}

void sol() {
	cin >> n >> k;
	_for(i, n) cin >> P[i];
	double l = 0, r = 1;
	while (1) {
		Numerator = -1, Denominator = -1;
		double mid = (l + r) / 2;
		LL cnt = find(mid);
		if (cnt == k) break;
		if (cnt < k) r = mid;
		else l = mid;
	}
	cout << Numerator << "/" << Denominator << "\n";
}

int main() {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	//freopen("in.txt", "r", stdin);

	sol();
	return 0;
}
C语言版代码
#include <stdio.h>
#include <stdlib.h>
#define maxn 1005
#define _for(i, a) for(LL i = 0; i < (a); ++i)
typedef long long LL;

LL n, k;
LL P[maxn];
LL Numerator, Denominator;	//最接近mid的分子和分母

LL find(double mid) {
	LL cnt = 0;
	_for(i, n) {
		LL num = mid * P[i];	//向下取整
		cnt += P[i] - num - 1;	//比mid小的分数
		if (Numerator == -1 && Denominator == -1 || Numerator * P[i] > (num + 1) * Denominator) {	//保存分子和分母
			Numerator = num + 1;
			Denominator = P[i];
		}
	}
	return cnt;
}

void sol() {
	scanf("%lld%lld", &n, &k);
	_for(i, n) scanf("%lld", &P[i]);
	double l = 0, r = 1;
	while (1) {
		Numerator = -1, Denominator = -1;
		double mid = (l + r) / 2;
		LL cnt = find(mid);
		if (cnt == k) break;
		if (cnt < k) r = mid;
		else l = mid;
	}
	printf("%lld/%lld\n", Numerator, Denominator);
}

int main() {
	sol();
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值