【CF#581(Div.2)】A&B
date:2019.8.20
我第一次打CF的比赛,感觉题目非常新颖并且非常棒,但是由于翻译软件不太给力所以我对题意的理解也不是很透彻,最后我做出了前两道题,感觉还可以吧
A.
Description
给定一个用二进制表示的数n,求小于n的4的正整数次幂的个数
其中$n\leq 2^{100}$
Solution
规律探索题
显然暴力计算是不可取的
我们观察一些性质,4的正整数次幂用二进制表示一定是1000....0(其中0有偶数个)
我们可以继续找规律:样例100000000的结果是4,也就是1后面数字的个数的一半!
其实100000000是一个特例,因为它本身就是一个4的正整数次幂,所以不能计算自身
假定现在的数是100000001,那么答案就是5(多了一个100000000)
也就是总结出了一个规律:如果最高位1后面的数字有偶数个,那么答案就是后面数字个数的一半加一(全都是0的情况要特判一下,不需要加一)
那么奇数个呢?
显然,最高位后面的数字个数为奇数个时这个数不可能是一个4的正整数次幂,并且小于这个数的最大的4的正整数次幂就是1000...0(位数为原数-1)
那么我们直接将这个数变成一个位数少1,并且全都是1的数,这样不影响答案,并且这样我们就能利用上述的规律求解了
PS:规律是我现场推出来的,思路比较乱请见谅
Code
#include <bits/stdc++.h> using namespace std; typedef long long ll; char s[110]; int main() { scanf("%s", s + 1); int n = strlen(s + 1); if (n % 2 == 0) { for (register int i = 1; i < n; ++i) s[i] = '1'; n--; } int pd = 0; for (register int i = 2; i <= n; ++i) if (s[i] == '1') {pd = 1; break ;} if (pd == 1) printf("%d\n", (n - 1) / 2 + 1); else printf("%d\n", (n - 1) / 2); return 0; }
B.
Description
给定n,l,r,构造一个序列满足如下条件并且求出符合条件的序列中元素之和最大和最小的序列的元素和
- 序列中有n个元素
- 序列中不同元素的个数在$[l,r]$范围内
- 序列中只允许出现1或者是偶数
- 如果序列中存在一个数为偶数,那么必须满足$\frac{n}{2}$同时出现在序列中
Solution
思考最后一条限制,假设当前数是一个偶数,那么这个数的二分之一也要出现,一个数除以二可能是奇数也可能是偶数,如果是奇数那么只能是1(限制3),如果是偶数那么意味着这个数一半的一半依然要出现……
所以我们可以联想出这个序列的样子:1,2,4,8,16……,即二的正整数次幂组成的序列
如果我们要构造元素和最小的序列,也就是在满足前两条限制的情况下让1尽可能的多
如果我们要构造元素和最大的序列,也就是在满足前两条限制的情况下让最大的数尽可能的多
通过简单的手玩样例就能确定循环边界
代码也就很简单了
Code
#include <bits/stdc++.h> using namespace std; int n, l, r; int maxx[1010], minn[1010]; int main() { scanf("%d%d%d", &n, &l, &r); for (int i = 1; i <= n - l + 1; ++i) minn[i] = 1; for (int i = n - l + 2; i <= n; ++i) minn[i] = minn[i - 1] << 1; int ans1 = 0; for (int i = 1; i <= n; ++i) ans1 += minn[i]; maxx[1] = 1; for (int i = 2; i <= r - 1; ++i) maxx[i] = maxx[i - 1] << 1; for (int i = r; i <= n; ++i) maxx[i] = maxx[r - 1] << 1; int ans2 = 0; for (int i = 1; i <= n; ++i) ans2 += maxx[i]; if (l == r && l == 1) ans2 = ans1; printf("%d %d\n", ans1, ans2); return 0; }