>
一个无限长的楼梯上站着两个人,其中一个人在第 级台阶上,另一个人在第 级台阶上。
每个人移动 次的时候消耗的体力为 ,每一次可以向上或向下移动 阶台阶,求两人相遇时消耗体力之和的最小值。
考虑贪心策略。
显然,两人相向而行是需要的移动次数最少。同时可以发现两人移动的步数越接近得到的解越优(即移动的步数越平均越好),按这样的策略移动可以得到答案。
设移动的总步数为 ,则 ,则第一个人移动的步数为 ,第二个人移动的步数为 (或者 ),两人消耗的体力分别为 、 。求和即可。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long ll;
int a, b;
int main()
{
cin >> a >> b;
if (a > b) swap(a, b);
int s = b - a;
int t1 = s / 2;
int ans = t1 * (t1 + 1) / 2 + (s - t1) * (s - t1 + 1) / 2;
cout << ans << endl;
return 0;
}
给定一个整数 ,可以无限制的选取重量在 之间的砝码,求可以凑出 任意正整数所需要的最少的砝码数量。
与第一题相比难度跨跃不大,门槛在读题。考察的是二进制。冷静思考一下还是很容易的。
任何一个数都可以由 的整数次幂之和构成,因此,只需要求出 的二进制表示下为 的最高位,将这一位到第 位所代表的 的整数次幂全部选上,则 之间所有的重量都可以凑出。
代码运用了一点小技巧,可能会难理解一些。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
int n, ans;
string tobin(int x) // 将 10 进制的 x 转换为 2 进制。
{
string res;
while (x > 0) res += to_string(x % 2), x /= 2;
return res;
}
int main()
{
cin >> n;
string bin = tobin(n);
for (ans = bin.size() - 1; ans >= 0; ans -- ) // 倒序枚举
if (bin[ans] == '1') break;
cout << ans + 1 << endl;
return 0;
}
有一个整数数列 ,给出数列的前两项,其它项可以通过如下递推式求出:
给定一个可能 非常大 的正整数 和两个不大的正整数 ,分别计算并输出 对 取模的值。
前三个测试点满足 。
所有测试点满足 ,,,。
注意, 最多有 位数。
递推式其实是广义斐波那契数列。
这题是这场比赛的压轴题。
可以发现,在模意义下,斐波那契数列的第 项 仅取决于( 和 ),不难发现,这个二元组有 种取值,因此,在模意义下,斐波那契数列一定会最终产生循环,即当 足够大时,一定会出现 的情况。
由于 可能 非常大,所以需要采取特殊的取模方法,这里采用 秦九韶算法。
取模减法可能会出现负数,要用到取模防负小技巧:
这题细节很多,写的时候要注意一下。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define endl '\n'
const int N = 1e7 + 10, M = 3010;
string n;
int m, k, p, q;
int a[N], id[M][M];
void print(int a, int b)
{
for (int i = 0; i < k; i ++ )
{
int c = (p * b + q * a) % m;
a = b, b = c;
cout << a << endl;
}
}
int mod(int len)
{
int res = 0;
for (auto c : n) res = (res * 10 + c - '0') % len;
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> a[0] >> a[1] >> p >> q;
cin >> m >> k >> n;
a[0] %= m, a[1] %= m;
id[a[0]][a[1]] = 1;
for (int i = 2; i < N; i ++ )
{
a[i] = (p * a[i - 1] + q * a[i - 2]) % m;
auto& t = id[a[i - 1]][a[i]];
if (t && n.size() > 7)
{
int len = i - t;
int r = (mod(len) - t % len + len) % len; // 取模减法防负技巧。
print(a[t + r], a[t + r + 1]);
break;
}
t = i;
}
if (n.size() <= 7) print(a[stoi(n)], a[stoi(n) + 1]);
return 0;
}
Yih 使用下面代码构造了一颗线段树:
void build(int x, int l, int r) {
vis[x] = 1;
if(l == r) return;
int mid = (l + r) >> 1;
build(x << 1, l, mid);
build(x << 1 | 1, mid + 1, r);
}
对于一个数 ,若存在一个 满足执行完 build(1,1,n)
后 是最后一个被创建的节点,则称 为 Yuzu 数。
Yih 想让你帮忙探索 Yuzu 数的性质。
有 组询问,每组询问给定一个正整数 ,对于每一组询问,输出 是否为 Yuzu 数。
暴力出奇迹,打表拿省一。再次印证。
直接看题没什么思路,看到数据范围超级大,想到打表找规律,把上面那段线段树代码搞上,发现有这样一个规律:
打表发现二进制下每一个 Yuzu 数都是由一堆 后面接着一堆 最后再加上一个 构成的,为了方便,我们去掉最后一个 ,于是就很简单了。
没什么细节,很轻松就过掉了。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int t, n;
int main()
{
cin >> t;
while (t -- )
{
string bin;
cin >> n;
bool flag = n % 2;
while (n) bin += to_string(n % 2), n /= 2;
reverse(bin.begin(), bin.end());
bin.pop_back();
while (bin.back() == '0') bin.pop_back();
for (int i = 0; i < bin.size(); i ++ )
if (bin[i] == '0') flag = false;
puts(flag ? "Y" : "N");
}
return 0;
}
然后就 了。