VP Codeforces Round 1012 (Div. 2)
A. Treasure Hunt
点击查看代码
void solve() {
int x, y, a;
std::cin >> x >> y >> a;
a %= (x + y);
if (a < x) {
std::cout << "NO\n";
} else {
std::cout << "YES\n";
}
}
B. Pushing Balls
题意:一个\(n\times m\)的矩阵,每次从某一行的最左边或者某一列的最上面推进来一个\(1\),也就是说这个\(1\)会把这个方向挨着它的\(1\)都推一格。现在给你一格矩阵,判断能不能通过一些操作得到它。
每个1左边或者右边肯定是一段连续的1一直到边界。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<std::string> s(n);
for (int i = 0; i < n; ++ i) {
std::cin >> s[i];
}
std::vector st(n, std::vector<int>(m));
for (int i = 0; i < n; ++ i) {
int flag = 1;
for (int j = 0; j < m; ++ j) {
if (s[i][j] == '0') {
flag = 0;
}
st[i][j] |= flag;
}
}
for (int j = 0; j < m; ++ j) {
int flag = 1;
for (int i = 0; i < n; ++ i) {
if (s[i][j] == '0') {
flag = 0;
}
st[i][j] |= flag;
}
}
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < m; ++ j) {
if (s[i][j] == '1' && !st[i][j]) {
std::cout << "NO\n";
return;
}
}
}
std::cout << "YES\n";
}
C. Dining Hall
题意:\((x, y)\)代表一组,这一组有\(4\)个位置,分别是\((3x+1,3y+1), (3x+1,3y+2), (3x+2,3y+1), (3x+2,3y+2)\)。现在有两种类型的人,第一种类型的人会走到距离最近的空位置坐下,另一个类型的人会选择没有一格位置有人坐的且左下角最近的组的位置坐下。求每个人坐的位置的下标。距离定义为曼哈顿距离,每组右上角的距离要加上2。
这题难在读题。
我们用两个\(set\)维护,一个\(set\)存空的组的位置,一个存没人坐但是组内有其他位置被坐了的位置。那么我们只需要每次在有人坐了一个新组的情况把其它位置都加到第二个\(set\)里就行。每个下标模\(3\)后就得到了它所在的组。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::set<std::array<int, 3>> s1, s2;
for (int i = 0; i + 1 <= n; ++ i) {
for (int j = 0; (i + 1) * (j + 1) <= n; ++ j) {
for (int x = 1; x <= 2; ++ x) {
for (int y = 1; y <= 2; ++ y) {
s1.insert({3 * i + 3 * j + x + y + 2 * (x == 2 && y == 2), 3 * i + x, 3 * j + y});
}
}
}
}
auto work = [&](int x, int y) -> void {
int r = x / 3, c = y / 3;
for (int i = 1; i <= 2; ++ i) {
for (int j = 1; j <= 2; ++ j) {
if (!s1.count({3 * r + 3 * c + i + j + 2 * (i == 2 && j == 2), 3 * r + i, 3 * c + j})) {
continue;
}
if (3 * r + i == x && 3 * c + j == y) {
continue;
}
s1.erase({3 * r + 3 * c + i + j + 2 * (i == 2 && j == 2), 3 * r + i, 3 * c + j});
s2.insert({3 * r + 3 * c + i + j + 2 * (i == 2 && j == 2), 3 * r + i, 3 * c + j});
}
}
};
for (int i = 0; i < n; ++ i) {
if (a[i] == 0) {
auto [d, x, y] = *s1.begin();
std::cout << x << " " << y << "\n";
s1.erase(s1.begin());
work(x, y);
} else {
if (s2.size() && *s2.begin() < *s1.begin()) {
auto [d, x, y] = *s2.begin();
s2.erase(s2.begin());
std::cout << x << " " << y << "\n";
} else {
auto [d, x, y] = *s1.begin();
std::cout << x << " " << y << "\n";
s1.erase(s1.begin());
work(x, y);
}
}
}
}
D. Simple Permutation
题意:要求构造一个排列,使得\(i \in [1, n], \lceil \frac{\sum_{i=1}^{i} p_i}{i} \rceil\)为质数的位置至少有\(\lfloor \frac{n}{3} \rfloor - 1\)个。
乱搞搞过的。
如果要让\(\lceil \frac{\sum_{i=1}^{i} p_i}{i} \rceil\)为质数,那么假设\([1, i-1]\)的和为\(sum\),并且我们得到的质数为\(P\),那么\(p_i\)的取值范围为\([i \times P - sum, i \times (P + 1) - 1]\),我们可以用\(set\)存数,然后找满足条件的最小的数。\(P\)如何确定?可以取大于等于\(n\)的第一个质数,然后如果\(sum\)减少当前可以用的最小值除\(i\)大于\(P\),\(P\)就跳到下一个质数。
点击查看代码
const int N = 1e6 + 5;
std::vector<int> primes;
bool st[N];
void init(int n) {
for (int i = 2; i <= n; ++ i) {
if (!st[i]) {
primes.push_back(i);
}
for (auto & p : primes) {
if (p * i > n) {
break;
}
st[p * i] = true;
if (i % p == 0) {
break;
}
}
}
}
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
std::set<int> s;
for (int i = 1; i <= n; ++ i) {
s.insert(i);
}
int p = std::ranges::upper_bound(primes, n) - primes.begin();
-- p;
a[0] = primes[p];
s.erase(a[0]);
i64 sum = a[0];
int cnt = 1;
for (int i = 2, j = 0; i <= n; ++ i) {
while (j < primes.size() && (sum + *s.begin() + i - 1) / i > primes[j]) {
++ j;
}
if (j == primes.size()) {
a[i - 1] = *s.begin();
s.erase(s.begin());
continue;
}
i64 l = (i64)i * primes[j], r = (i64)i * (primes[j] + 1) - 1;
auto it = s.lower_bound(l - sum);
if (it == s.end() || sum + *it > r) {
a[i - 1] = *s.begin();
sum += *s.begin();
s.erase(s.begin());
} else {
++ cnt;
a[i - 1] = *it;
sum += *it;
s.erase(it);
}
}
// std::cout << cnt << "\n";
assert(cnt >= n / 3 - 1);
for (int i = 0; i < n; ++ i) {
std::cout << a[i] << " \n"[i == n - 1];
}
}