2021.11.27【提高组】模拟赛C组

2021.11.27【提高组】模拟赛C组

T1.蚂蚁
Description:
n只蚂蚁以每秒1cm的速度在长为Lcm的竿子上爬行。当蚂蚁爬到竿子的端点时就会掉落。由于竿子太细,两只蚂蚁相遇时,它们不能交错通过,只能各自反向爬回去。对于每只蚂蚁,我们知道它距离竿子左端的距离xi,但不知道它当前的朝向。请计算各种情况当中,所有蚂蚁落下竿子所需的最短时间和最长时间。

Input:
第一行:N,L
后N行 每行一个Ai,表示位置

Output:
最短时间和最长时间

Solution:
最短时间的计算,可以算出每只蚂蚁距离两端点最近的距离,也就是比较到左端点和右端点的距离,最短时间即为所有蚂蚁的到其最近端点的距离中的最大值
最长时间同理,即为每只蚂蚁到其最远端点的距离中的最大值

题目中提到两只蚂蚁相撞之后会反方向爬,其实这是可以忽略的,相撞之后可以视为两蚂蚁交换位置后继续向原方向走

Code:

#include <stdio.h>
#include <iostream>
#include <cmath>
#include <cstring>

#define N 1000000
#define re register
#define ll long long

using namespace std;

ll n, l, a[N], ans1, ans2;

signed main () {
	//freopen("t1.in", "r", stdin);
	//freopen("t1.out", "w", stdout);
	scanf("%lld %lld", &n, &l);
	for (re ll i (1); i <= n; ++ i) {
		scanf("%lld", &a[i]);
		ans1 = max(ans1, min(a[i], l - a[i]));
		ans2 = max(ans2, max(a[i], l - a[i]));
	}
	printf("%lld %lld", ans1, ans2);
}

T2 max:

Description:
一个正整数一般可以分为几个互不相同的自然数的和,如3=1+2,4=1+3,5=1+4=2+3,6=1+5=2+4,…。
现在你的任务是将指定的正整数n分解成m个(m>=1)互不相同的自然数的和,且使这些自然数的乘积最大。

Input:
N(N <= 10000)

Output:
第一行是分解方案,相邻的数之间用一个空格分开,并且按由小到大的顺序。
第二行是最大的乘积。

Solution:
1:(80pts)我们可以发现,只要被拆分出来的数,差尽可能的小,就可以做到积最大,同时要避免出现1,因为1不仅占了一个数的位置,还对积的大小没有任何贡献,所以我们可以从2开始装入答案,每次加1,直到总和超过N,最后可能会出现超过N的情况,于是我们可以去掉一个数,使得剩下来的总和正好等于N,去掉的数即为total - N,若我们用一个数组来存每个拆分出来的数,则他的下标为total - N - 1(从2开始装入,故所有数对应的下标为它本身-1),当然该方法弊端很明显,我们不能确保每次都有一个数用总和减去后都正好等于N,也就是说减去后可能会导致总和小于N
主要部分代码如下:

	for (re ll i (2); ; ++ i) {
		if (tot >= n) {
			break;
		}
		a[++ t] = i;
		tot += i;
	}
	a[tot - n - 1] = 0;
	for (re ll i (1); i <= t; ++ i) {
		if (a[i] != 0) {
			time(a[i]);
			printf("%lld ", a[i]);
		}
	}

2.(100pts)该方法就是在第一种方法的基础上,在删除数的部分加以改动,首先我们不一定能找到一个减去后正好等于N的数,所以我们枚举先列出来的数,找到用总和减去后与N差距最小的一个(若存在减去后等于N的则直接删去那个),删去那个找到的数(即赋为0),然后就会出现总和小于N的情况,此时就可以算出总和与N的差(min),然后从后往前将每个数都加1,直到分完min个
最后,由于答案较大,需要加高精度累乘
Code:

#include <stdio.h>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>

#define N 10000000
#define re register
#define ll long long

using namespace std;

ll n, a[N], t, tot, ans1[N], w = 1, xx, minc = 1e15, idx;
bool wz = false;

void time(int x) {
	for(re ll i (1); i <= w; ++ i) {
		ans1[i] = ans1[i] * x;
	}
	for(re ll i (1); i <= w; ++ i) {
		if(ans1[i] >= 10) {
			xx = ans1[i] / 10;
			ans1[i + 1] += xx;
			ans1[i] %= 10;
			w = max(w, i + 1);
		}
	}
}

signed main () {
//	freopen("max.in", "r", stdin);
//	freopen("max.out", "w", stdout);
	scanf("%lld", &n);
	ans1[1] = 1;
	for (re ll i (2); ; ++ i) {
		if (tot >= n) {
			break;
		}
		a[++ t] = i;
		tot += i;
	}
	if (tot != n) {
		for (re int i (1); i <= t; ++ i) {
			if (tot - a[i] == n) {
				a[i] = 0;
				tot -= a[i];
				wz = true;
			} else if (abs((tot - a[i]) - n) < minc) {
				minc = abs((tot - a[i]) - n);
				idx = i;
			}
		}
		if (wz == false) {
			tot -= a[idx];
			a[idx] = 0;
		}
		if (tot < n) {
			for (re int i (n); i >= 1; -- i) {
				if (a[i] != 0 and minc != 0) {
					a[i] ++; 
					minc --;
				}
			}
		}
	}
		for (re ll i (1); i <= t; ++ i) {
			if (a[i] != 0) {
				time(a[i]);
				printf("%lld ", a[i]);
			}
		}
	printf("\n");
	for (re ll i (w); i >= 1; -- i) {
		printf("%lld", ans1[i]);
	}
}

T3 围攻:

Description:
经过刘邦的严密缉查,项羽的位置也就水落石出了。刘邦便趁机集合军队,进行对项羽的围攻。为了增加胜率,张良研究出一种全新的战法,目的就是一举打败难缠的项羽。
这种军队共有N个单位,一个接着一个排成一排,每个单位可以是士兵,或者是战车,这样的组合可以爆发出意想不到的强大战斗力;但有一点,两辆战车不能相邻,否则会发生剐蹭等不好的事故。刘邦希望知道这N个单位的军队都多少种不同的排列方法,以便在战场中随机应变。两种军队的排列方法是不同的,当且仅当某一个单位对应不同,如:第i位这种是士兵,那种是战车……

Input:
N

Output:
方案数(取模10^8+7)

Solution:
1.(70pts) 可以dp推导,用f[i][0]表示第i个位置放人的方案数,f[i][1]表示第i位放车的方案数,放人的没有限制,于是第i位方案数为i-1位放人和放车的方案数的和
(f[i][0] = f[i - 1][0] + f[i - 1][1])
而车不能连着放,所以他只能等于i-1的位置上放人的方案数
以下为主要代码:

f[1][1] = f[1][0] = 1;
for (int i = 2; i <= n; ++ i) {
	f[i][0] = f[i - 1][1] + f[i - 1][0];
	f[i][1] = f[i - 1][0];
}

如果我们多尝试几个数据,就会发现其实这是个斐波那契数列!
于是就有了一下代码:

#include <stdio.h>
#include <iostream>
#include <cmath>
#include <cstring>

#define re register
#define ll long long
#define MOD 100000007

#pragma GCC optimize("Ofast")

using namespace std;

ll n, a = 2, b = 3, c;

signed main () {
//	freopen("siege.in", "r", stdin);
//	freopen("siege.out", "w", stdout);
	scanf("%lld", &n);
	n = (n - 1) % 200000016 + 1;
	switch(n) {
		case 1: printf("2"); return 0;
		case 2: printf("3"); return 0;
	}
	for (re ll i (3); i <= n; ++ i) {
		c = (a + b) % MOD;
		a = b % MOD;
		b = c % MOD;
	}
	printf("%lld", c);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值