Codeforces Round 919 (Div. 2)
https://codeforces.com/contest/1920
B还行,C、E good(E据说是很典的dp但我是dp苦手),D、F1无聊,F2不会
A. Satisfying Constraints *800
有 \(n\) 个条件,每个条件形如 \(x\ge k,x\le k\) 或 \(x\neq k\),\(k\) 为整数。问满足条件的整数 \(x\) 的个数。
先处理 \(\ge,\le\),得到限制区间的交集,最后把区间中 \(\neq\) 的减去。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void sol() {
int n;
cin >> n;
int l = -1e9, r = 1e9;
vector<int> neq;
while (n--) {
int t, x;
cin >> t >> x;
if (t == 1) l = max(l, x);
else if (t == 2) r = min(r, x);
else neq.push_back(x);
}
int ans = max(0, r - l + 1);
for (int x : neq) ans -= x >= l && x <= r;
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int T; cin >> T; while (T--)
sol();
}
B. Summation Game *1100
给定正整数数组。Alice先移除不超过 \(k\) 个数,然后Bob把不超过 \(x\) 个数取成相反数,然后游戏结束。Alice想最大化所有数的和 \(sum\),Bob想最小化 \(sum\),问 \(sum\) 最后会是多少。
Bob肯定会取前 \(x\) 大的数,为了减少损失,Alice也得移除最大的若干个数(不一定要移除 \(k\) 个)。从大到小排序数组,枚举Alice移除了几个数,前缀和算算答案即可。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void sol() {
int n, k, x;
cin >> n >> k >> x;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++)
cin >> a[i];
sort(next(a.begin()), a.end(), greater<int>());
for (int i = 1; i <= n; i++)
a[i] += a[i - 1];
int ans = -1e9;
for (int i = 0; i <= k; i++) {
int j = min(n, i + x);
ans = max(ans, -a[j] + a[i] + a[n] - a[j]);
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int T; cin >> T; while (T--)
sol();
}
C. Partitioning the Array *1600 暴露智商了,没做出来555
给定长度为 \(n\) 的数组,对 \(n\) 的每个因子 \(d\),把数组分成 \(n/d\) 个长度为 \(d\) 的连续子列,若存在 \(m\ge 2\) 使得每个连续子列在 \(\mod m\) 意义下完全相同,则答案加一。问答案是多少。
\(n\le 10^5, 1\le a_i\le n\)
转化成 \(\forall i\in[1,d]\),\(\exists m\ge 2\),使得 \(\ a_i\equiv a_{i+d}\pmod m\)
也就是 \(a_{i+d}-a_i\equiv 0\pmod m\)
也就是 \(m|(a_{i+d}-a_i)\)
\(m\) 取这些数的 \(\gcd\) 即可,也就是
检查 \(\gcd\) 是否为 \(1\) 就行。\(O(\sqrt n n\log n)\),注意减法可能出现负数。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int P = 998244353;
void sol() {
int n;
cin >> n;
vector<int> a(n);
for (int &x : a) cin >> x;
vector<int> divs;
for (int i = 1; i <= n / i; i++) {
if (n % i) continue;
divs.push_back(i);
if (i != n / i) divs.push_back(n / i);
}
int ans = 0;
for (int d : divs) {
int g = 0;
for (int i = 0; i < d; i++)
for (int j = i; j < n; j += d)
g = __gcd(g, a[j] - a[i]);
if (g != 1 && g != -1) ans++; //可能负数
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int T; cin >> T; while (T--)
sol();
}
D. Array Repetition *1900 模拟
开始数组为空,两种操作:在末尾添一个数、把整个数组复制 \(x\) 次到末尾。\(q\) 次询问某个位置 \(k\) 上的数是什么。
\(n,q\le 10^5, k_i\le 10^{18}\)
模拟。注意越界处理,别爆longlong。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void sol() {
int n, q;
cin >> n >> q;
map<ll, ll> mp;
ll idx = 0;
vector<ll> pos;
for (int i = 0, f = 0; i < n; i++) {
ll t, x;
cin >> t >> x;
if (f) continue;
if (t == 1) {
pos.push_back(++idx);
mp[idx] = x;
} else {
if (idx > (ll)1e18 / (x + 1)) {
f = true;
continue;
}
idx *= x + 1;
}
}
while (q--) {
ll k;
cin >> k;
ll ans = mp[k];
while (!ans) {
ll las = *prev(upper_bound(pos.begin(), pos.end(), k));
k = (k - 1) % las + 1;
ans = mp[k];
}
cout << ans << ' ';
}
cout << '\n';
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int T; cin >> T; while (T--)
sol();
}
E. Counting Binary Strings *2100 得加练dp了
定义“好串”:只有一个 \(1\) 的 \(01\) 串。
问恰有 \(n\) 个好子串且所有好子串的长度 \(\le k\) 的 \(01\) 串数量。
\(1\le k\le n\le 2500\)
形如 000100000 的子串对答案的贡献为 \((x+1)(y+1)\),其中 \(x,y\) 分别为左右两边 \(0\) 的数量。
dp。\(f(i,j)\) 表示有 \(i\) 个好子串,最后一段 \(0\) 的长度为 \(j\) 的串的数量。(\(1\) 和总串长完全不用管)
枚举最后一段 \(0\) 的数量 \(k\),则 \(f(i,j)+=f(i-(j+1)(k+1),k)\)
注意初值 \(f(0,0)=f(0,1)=\cdots=1\)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int P = 998244353;
void sol() {
ll n, K;
cin >> n >> K;
vector f(n + 1, vector<ll>(K));
fill(f[0].begin(), f[0].end(), 1);
for (int i = 1; i <= n; i++) {
for (int j = 0; j < K; j++) {
for (int k = 0; j + k + 1 <= K && i - (j + 1) * (k + 1) >= 0; k++) {
(f[i][j] += f[i - (j + 1) * (k + 1)][k]) %= P;
}
}
}
cout << accumulate(f[n].begin(), f[n].end(), 0ll) % P << '\n';
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int T; cin >> T; while (T--)
sol();
}
F1. Smooth Sailing (Easy Version) *2500 好想难写的简单二分答案+bfs
给定图,每个点是海底火山/岛屿/海洋。岛屿是个四连通区域且不与边界接壤,火山和海洋都能行船。
环岛路线:由非岛屿点组成,环绕所有岛屿,使岛屿与边界不能八连通。
环岛路线的安全度:路线中每个点到每座火山的曼哈顿距离的最小值。
\(q\) 次询问,问从某点出发的环岛路线的安全度最大是多少。\(q\le 5\)
二分答案 \(mid\),把起点所在的安全度 \(\ge mid\) 的非岛屿四连通区域标出,看这个区域能把不能把岛屿的边界包含在内
也就是把上述区域ban掉,看边界与岛屿是否八连通即可。代码很长
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int dx[] = {0,0,1,-1,1,-1,-1,1};
const int dy[] = {1,-1,0,0,1,1,-1,-1};
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int n, m, q;
cin >> n >> m >> q;
vector g(n + 1, vector<char>(m + 1));
queue<pair<int, int>> que;
vector dis(n + 1, vector<int>(m + 1, -1));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> g[i][j];
if (g[i][j] == 'v') {
que.push({i, j});
dis[i][j] = 0;
}
}
}
while (!que.empty()) { //计算每个点到最近火山的曼哈顿距离
auto [x, y] = que.front(); que.pop();
for (int i = 0; i < 4; i++) {
int xx = x + dx[i], yy = y + dy[i];
if (xx < 1 || xx > n || yy < 1 || yy > m) continue;
if (dis[xx][yy] != -1) continue;
dis[xx][yy] = dis[x][y] + 1;
que.push({xx, yy});
}
}
while (q--) {
int x, y;
cin >> x >> y;
auto ok = [&](int mid) {
if (dis[x][y] < mid) return false;
vector f(n + 1, vector<int>(m + 1)); //f=1与(x,y)四连通,f=2与边界八连通
f[x][y] = 1;
queue<pair<int, int>> q;
q.push({x, y});
while (!q.empty()) {
auto [x, y] = q.front(); q.pop();
for (int i = 0; i < 4; i++) {
int xx = x + dx[i], yy = y + dy[i];
if (xx < 1 || xx > n || yy < 1 || yy > m) continue;
if (f[xx][yy] || dis[xx][yy] < mid || g[xx][yy] == '#') continue;
f[xx][yy] = 1;
q.push({xx, yy});
}
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if ((i == 1 || i == n || j == 1 || j == m) && !f[i][j])
q.push({i, j}), f[i][j] = 2;
while (!q.empty()) {
auto [x, y] = q.front(); q.pop();
for (int i = 0; i < 8; i++) {
int xx = x + dx[i], yy = y + dy[i];
if (xx < 1 || xx > n || yy < 1 || yy > m) continue;
if (g[xx][yy] == '#') return false;
if (f[xx][yy]) continue;
f[xx][yy] = 2;
q.push({xx, yy});
}
}
return true;
};
int l = 0, r = 2e5, ans = 0;
while (l <= r) {
int mid = l + r >> 1;
if (ok(mid)) ans = mid, l = mid + 1;
else r = mid - 1;
}
cout << ans << '\n';
}
return 0;
}