ICPC 2019 陕西省赛
L. Digit Product
题目大意
定义f(x)为各个位数之积,给定lr,求 $ \prod_{i=l}^{r} f(i) \mod (10^9 + 7)$
解题思路
简单观察可以发现,只有数位中有0答案就是0,所以最多只会跑9个数字,遇到0剪枝退出即可
代码实现
#include <bits/stdc++.h>
using i64 = long long;
const int MOD = 1e9 + 7;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int tt;
std::cin >> tt;
while (tt--) {
i64 l, r, ans = 1;
std::cin >> l >> r;
for (int i = l; i <= r && ans; i++) {
std::string s = std::to_string(i);
for (auto ch : s) {
ans = (ans * (ch - '0')) % MOD;
if (ans == 0) {
break;
}
}
}
std::cout << ans << "\n";
}
}
F. K-hour Clock
题目大意
定义一天是k小时(k-1的下一个时刻为0),给定当前时间x,y小时后是z点,输出任意一个满足的k,不可能则输出-1
解题思路
分类讨论x+y和z的关系即可
代码实现
#include <bits/stdc++.h>
using i64 = long long;
const int MOD = 1e9 + 7;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int tt;
std::cin >> tt;
while (tt--) {
i64 x, y, z, ans;
std::cin >> x >> y >> z;
if (z > x + y) {
ans = -1;
} else if (z == x + y) {
ans = z + 1;
} else {
ans = x + y - z;
if ((x + y) % ans != z || ans <= x) {
ans = -1;
}
}
std::cout << ans << "\n";
}
}
E. Turn It Off
题目大意
给定长为n的01串,可以选择k次长为l的区间将1变为0,问最短的l是多少
解题思路
二分这个区间长度即可,特判全是1的情况
代码实现
#include <bits/stdc++.h>
using i64 = long long;
const int MOD = 1e9 + 7;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int tt;
std::cin >> tt;
while (tt--) {
int n, k;
std::cin >> n >> k;
std::string s;
std::cin >> s;
if (!std::count(s.begin(), s.end(), '1')) {
std::cout << 0 << "\n";
continue;
}
auto check = [&](int x) -> bool {
int cnt = 0;
for (int i = 0; i < n; i++) {
if (s[i] == '1') {
i += x - 1;
cnt++;
}
}
return cnt <= k;
};
int l = 1, r = n, ans = 0;
while (l <= r) {
int mid = (l + r) / 2;
if (check(mid)) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
std::cout << ans << "\n";
}
}
B. Grid with Arrows
题目大意
给你两个n*m的矩阵ab,\(a_{ij}\)表示在该位置的移动方向,\(b_{ij}\)表示在该位置的移动距离,问是否存在一个位置使得从该位置出发可以遍历整个矩阵
解题思路
将每个格子视作一个节点,根据方向和距离建有向图,从入度最小的点开始dfs即可
代码实现
#include <bits/stdc++.h>
using i64 = long long;
const int MOD = 1e9 + 7;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int tt;
std::cin >> tt;
while (tt--) {
int n, m, x = 0;
std::cin >> n >> m;
std::vector<std::string> a(n);
std::vector<std::vector<int>> b(n, std::vector<int>(m)), c(n, std::vector<int>(m)), g(n * m);
std::vector<int> in(n * m), vis(n * m);
for (int i = 0; i < n; i++) {
std::cin >> a[i];
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
std::cin >> b[i][j];
c[i][j] = x++;
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (a[i][j] == 'l' && j - b[i][j] >= 0 && j - b[i][j] < m) {
g[c[i][j]].push_back(c[i][j - b[i][j]]);
in[c[i][j - b[i][j]]]++;
} else if (a[i][j] == 'r' && j + b[i][j] >= 0 && j + b[i][j] < m) {
g[c[i][j]].push_back(c[i][j + b[i][j]]);
in[c[i][j + b[i][j]]]++;
} else if (a[i][j] == 'u' && i - b[i][j] >= 0 && i - b[i][j] < n) {
g[c[i][j]].push_back(c[i - b[i][j]][j]);
in[c[i - b[i][j]][j]]++;
} else if (a[i][j] == 'd' && i + b[i][j] >= 0 && i + b[i][j] < n) {
g[c[i][j]].push_back(c[i + b[i][j]][j]);
in[c[i + b[i][j]][j]]++;
}
}
}
int cnt = 0, st, minn = 2e9;
auto dfs = [&](auto &&self, int u) -> void {
if (!vis[u]) {
vis[u] = 1;
for (auto v : g[u]) {
self(self, v);
}
cnt++;
}
};
for (int i = 0; i < n * m; i++) {
if (in[i] < minn) {
minn = in[i];
st = i;
}
}
dfs(dfs, st);
if (cnt != n * m) {
std::cout << "No\n";
} else {
std::cout << "Yes\n";
}
}
}
C. 0689
题目大意
给定一个只包含0689四种字符的字符串,对任意非空子串进行至多一次180度旋转(例如0689变6890),问最后能有多少个不一样的字符串
解题思路
首尾是00,88,69的子串删除首尾之后对答案没有影响,统计之后去掉即可,特判全是6或9的情况,翻转不会变,需要对答案-1
代码实现
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int tt;
std::cin >> tt;
while (tt--) {
std::string s;
std::cin >> s;
std::vector<i64> a(10);
for (auto ch : s) {
a[ch - '0']++;
}
i64 ans = s.size() * (s.size() + 1) / 2 + 1;
ans -= a[0] * (a[0] + 1) / 2;
ans -= a[8] * (a[8] + 1) / 2;
ans -= a[6] * a[9];
if (a[6] == s.size() || a[9] == s.size()) {
ans--;
}
std::cout << ans << "\n";
}
}
J. Coolbits
题目大意
给定n个区间,选n个数字,求这些数字按位与的最大值
解题思路
直接从高位开始贪心的填,先看所有的数字是否能在这一位填1,如果可以则直接填,否则再判断每个数字这一位填0还是1
代码实现
#include <bits/stdc++.h>
using i64 = long long;
const int MOD = 1e9 + 7;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int tt;
std::cin >> tt;
while (tt--) {
int n, ans = 0;
std::cin >> n;
std::vector<i64> l(n), r(n), x(n);
for (int i = 0; i < n; i++) {
std::cin >> l[i] >> r[i];
}
for (int i = 30; i >= 0; i--) {
int f = 1;
for (int j = 0; j < n && f; j++) {
if ((x[j] | (1 << i)) > r[j]) {
f = 0;
}
}
if (f) {
ans |= 1 << i;
for (int j = 0; j < n; j++) {
x[j] |= 1 << i;
}
} else {
for (int j = 0; j < n; j++) {
// 填上1之后在范围内并且这一位不填1后面全填1仍然在范围外,则这一位必须填1
if ((x[j] | (1 << i)) <= r[j] && (x[j] | ((1 << i) - 1)) < l[j]) {
x[j] |= 1 << i;
}
}
}
}
std::cout << ans << "\n";
}
}

浙公网安备 33010602011771号