edu175
A
题目大意
给定一个整数 \(n\) ,处理从 \(0\) 到 \(n\) 的所有整数。对于每一个整数 \(i\),\(i \bmod 3 = i \bmod 5\) ,则符合条件。
判断有多少个数符合条件。
思路
既要被 \(3\) 除有余数,又要被 \(5\) 除有余数 -> 余数在 \(0, 1, 2\)之间
0 1 2 3 4 5 6 7 8 9 10
√ √ √
11 12 13 14 15 16 17 18
√ √ √
通过上面的例子可以看出,\(15\) 为一个周期,i % 15 = 0 或 1 或 2 的都符合条件
代码
void solve()
{
ll n; cin >> n;
ll u = n / 15, sum = 0;
if(n % 15 > 2) u += 1, sum = u * 3; //相当于n = 18
else sum = u * 3 + (n % 15) + 1; //相当于n = 16
cout << sum << '\n';
}
B
题目大意
机器人初位置是点 \(x\) ,指令序列长度为 \(n\) ,由字符组成,其中 L 表示向左移动一个单位(从点 \(p\) 到点 \((p-1)\)),R 表示向右移动一个单位(从点 \(p\) 到点 \((p+1)\) )。
机器人开始执行这一连串指令(每秒执行一条指令,按指令出现的顺序进行)。每当机器人到达点 \(0\) 时,就会从头开始执行整个命令序列。一旦完成所有指令,就停止运行。
计算机器人在接下来的 \(k\) 秒内进入点 \(0\) 的次数。
思路
- 能否到达 \(0\)
- 在执行时是否出现位置为 \(0\)。若位置第一次为 \(0\),记录花了几秒;若没有出现过位置为 \(0\),直接返回 \(0\)
- 第一次到达 \(0\) 之后,能否再次到 \(0\)
- 从第一个指令开始,是否出现 \(l\) 的总数 等于 \(r\) 总数的情况。若出现过,记录下第一次出现这种情况的数值,那么 \(l\) \(+\) \(r\) 就是一个循环周期
- 计算循环次数
- 总秒数 \(k\) 减去 第一次到 \(0\) 花的秒数, 再除以循环周期并向上取整(模拟得出要向上取整)
注意
由于 \(k\) 的范围是 \(1\) ~ \(1e18\),在转换成 \(double\) 类型进行向上取整时就会损失精度,所以要手动向上取整
代码
void solve()
{
ll n, x, k; cin >> n >> x >> k;
string s; cin >> s;
ll qw = x;
ll l = 0, r = 0, pp = 0, dw = 0; //pp是第一次到0花了几秒,dw是循环周期
int id = 0, go1 = 0, go2 = 0, io = 1, z = 1;
for(int i = 0; i < n; i++) {
if(s[i] == 'L') l++, id--, x--;
else r++, id++, x++;
if(x == 0 && io == 1) go1 = 1, io = 0, pp = i + 1; //能否到达0
if(id == 0 && z == 1) go2 = 1, z = 0, dw = l + r; //后面能否进行循环
}
if(go1 == 0) {cout << 0 << '\n'; return;} //不能到达0
if(go2 == 0) {cout << 1 << '\n'; return;} //到0后不能进行循环
ll ans = k - pp;
//ll res = (ll)ceil(ans * 1.0 / dw);
ll res = (ans + dw - 1) / dw;
if(ans % dw == 0) res++; //模拟一下
cout << res << '\n';
}
C
题目大意
一条由 \(n\) 个单元格组成的长条,所有单元格最初都是红色的。
在一次操作中,可以选择一段连续的单元格并将其涂成蓝色。最多可以执行 \(k\) 次操作(可能为零)。
对于 \(i\) 单元,如果最后颜色不正确的话,惩罚就是 \(ai\)。
最终的惩罚是根据所有涂错颜色的单元格中最大惩罚来计算的。如果全部正确,则惩罚为 \(0\) 。
求最小惩罚值。
思路
由于是要求所有涂错的单元格中最大惩罚的最小值 --> 想到 二分
- 通过二分得到mid
- 碰到蓝色的格子,如果惩罚 > mid,就必须涂(不然最大惩罚就不是mid了)
- 由于一次可以涂一段区间,区间就从第一个涂蓝色的开始,直到下面这种情况截至
- 碰到红色的格子,如果惩罚 < mid,就不涂(因为涂了会浪费依次机会)
- 如果次数 > k的话就要返回false
- 说明 mid 选小了
- 如果次数 <= k的话就要返回true
- 说明 mid 选大了 或者 刚好
- 要求的是最小惩罚值
- 就返回 \(l\)
代码
bool check(vector<ll>& a, ll mid) {
ll cnt = 0;
for(int i = 0; i < n; ) {
if(s[i] == 'B' && a[i] > mid) { //碰到了必须要图的蓝色格子
cnt++;
int j = i; //遇到不符合条件的红色之前的蓝色都可以涂
while(j < n && (s[j] == 'B' || (s[j] == 'R' && a[j] <= mid))) j++;
i = j; //下次从不符合条件的这个格子开始
}
else i++;
}
if(cnt <= k) return true;
return false;
}
void solve()
{
cin >> n >> k;
cin >> s;
vector<ll> a(n);
ll ma = 0;
for(int i = 0; i < n; i++) cin >> a[i], ma = max(ma, a[i]);
ll l = 0 * 1ll, r = ma;
while(l <= r) {
ll mid = l + r >> 1;
if(check(a, mid)) r = mid - 1;
else l = mid + 1;
}
cout << l << '\n';
}

浙公网安备 33010602011771号