Codeforces Raif Round 1 (Div. 1 + Div. 2)
A
#include <bits/stdc++.h>
#define all(n) (n).begin(), (n).end()
#define se second
#define fi first
#define pb push_back
#define mp make_pair
#define sqr(n) (n)*(n)
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
typedef pair<ll, ll> PLL;
typedef vector<int> VI;
typedef double db;
template<class T1, class T2> bool umin(T1& a, T2 b) { return a > b ? (a = b, true) : false; }
template<class T1, class T2> bool umax(T1& a, T2 b) { return a < b ? (a = b, true) : false; }
template<class T> void clear(T& a) { T().swap(a); }
const int N = 2e5 + 5;
int n, m, _, k;
ll a[N], b[N];
int main() {
IOS;
for (cin >> _; _; --_) {
ll a, b, x, y; cin >> a >> b >> x >> y;
ll ans = abs(x - a) + abs(y - b) + 2;
if (x == a || y == b) ans -= 2;
cout << ans << '\n';
}
return 0;
}
B
char s[N];
int main() {
IOS;
for (cin >> _; _; --_) {
cin >> n >> s + 1;
int a = 0;
unordered_set<int> st;
rep (i, 1, n) {
if (s[i] == '-') st.insert(i % n), st.insert((i + 1) % n);
else if (s[i] == '>') {
if (a == 0) a = 1;
else if (a == 2) a = -1;
} else {
if (a == 0) a = 2;
else if (a == 1) a = -1;
}
}
if (a != -1) cout << n << '\n';
else cout << st.size() << '\n';
}
return 0;
}
C
贪心模拟
int main() {
IOS;
for (cin >> _; _; --_) {
string s; cin >> s;
int x = 0, y = 0, ans = 0;
bool f = 0;
for (auto c : s) {
if (c == 'A') {
if (f && y) ++x, y = 0, f = 0;
++x;
}
else if (c == 'B') {
if (x) --x, ++ans, f = 1;
else {
++y;
if (y == 2) ++ans, y = 0;
}
}
}
cout << s.size() - (ans << 1) << '\n';
}
return 0;
}
D
考虑到后效性, 倒着放, 同时对于行, 也是从 n 到 1, 把后效性消掉
我用的set只是为了图形优美, 什么容器都可
int a[N];
VI ans[N];
set<PII> st;
set<int> t;
int main() {
IOS; cin >> n; int res = 0; m = n;
rep(i, 1, n) cin >> a[i];
bool f = 1;
per(i, n, 1) {
if (a[i] == 0) continue;
if (a[i] == 1) ans[i].pb(m), ++res, st.insert({ m--, i });
else if (a[i] == 2) {
if (st.empty()) { f = 0; break; }
PII ls = *st.rbegin();
ans[i].pb(ls.fi); ++res;
st.erase(ls); t.insert(i);
}
else {
if (st.empty() && t.empty()) { f = 0; break; }
if (!t.empty()) {
int ls = *t.begin();
ans[i].pb(m); ++res;
ans[ls].pb(m--); ++res;
t.erase(ls); t.insert(i);
}
else {
PII ls = *st.rbegin();
ans[i].pb(m); ++res;
ans[ls.se].pb(m--); ++res;
st.erase(ls); t.insert(i);
}
}
}
if (!f || m < 0 || res > 2 * n) { cout << -1; return 0; }
cout << res << '\n';
rep(i, 1, n) for (auto j : ans[i]) cout << j - k << ' ' << i << '\n';
return 0;
}
E
优先队列
注意到, 将一个胡萝卜分的快数越多, 花费的时间越少, 就发现是优先队列, 每次选取 多分出一块 省的时间最多的那一根胡萝卜就行
ll a[N], b[N];
ll get(int x, int k) {
if (a[x] < k) return 1e9;
ll siz = a[x] / k, res = a[x] % k;
return sqr(siz + 1) * res + sqr(siz) * (k - res);
}
int main() {
IOS; cin >> n >> k;
priority_queue<PLI> q;
rep (i, 1, n) {
cin >> a[i], b[i] = 1;
q.push({ get(i, 1) - get(i, 2), i });
}
rep (i, n + 1, k) {
int x = q.top().se; q.pop();
++b[x]; q.push({ get(x, b[x]) - get(x, b[x] + 1), x });
}
ll ans = 0;
rep (i, 1, n) ans += get(i, b[i]);
cout << ans;
return 0;
}
F
//下标从1开始
dp(线段树也可), 常数大的O(n)
g[i] 表示 j~i (1 <= j <= i) 对答案的贡献, 那么最后的答案就是 \(\sum g[i]\)
明显 s[i] == '0', g[i] == g[i - 1],
对于 s[i] == '1', 讨论要复杂一些
因为是考虑区间最长的 '1', 理所应当我们要存个 t[i], 表示以 i 结尾的最长 '1', 毕竟在遍历 i 的时候, 区间最长 '1' 是会变的
显然 if s[i] == '0', t[i] = 0; else t[i] = t[i-1] + 1
当从 i - 1 到 i 的时候最后一段 '1' 变长了(s[i] == '1') 也就是说, 在上一段最后 t[i]个字符 到 (i - 1) - t[i - 1] 原本最长段是 t[i - 1](t[i - 1] == t[i] - 1)
然而现在成了 t[i], 也就说在这个区间内 多贡献了个区间长度, 对于 i-t[i]~i 也是多贡献了个区间长度
设 f[i] 表示 上一个长度为 i 的 '1' 串的起始位置, "1101" 在对于第4个位置的时候, 当前 '1' 段为 [4,4], 那么上一个 长度为 1 的段 是 [2, 2], 长度为2的段时[1, 2], 故 f[1] = 2, f[2] = 2
那么 s[i] == '1' 转移就已经出来了 g[i] = g[i + 1] + (i - t[i] - f[t[i]]) + t[i];
那么现在的问题成了怎么求 f[i], 显然 f[i] 对于 '1' 段是倒着的, "111110", 对于6位置 f[1] = 5, f[2] = 2, f[3] = 3, f[4] = 4, f[5] = 5;
其实已经发现了 f[i] = 上一个串的长度 - 正着数的位置, 比如对于 位置 i,
f[length(包含i的最长 '1' 的长度) - t[i] + 1] = i
至于长度嘛, 预处理O(n), 就行了, 虽然我用的并查集, 但还是O(1)的合并, 直接和 '1' 段的起始坐标最为祖先
然后我们又发现个问题, f[i] 随着 i 更新, 但我们计算 g[i] 的时候是想要未更新之前的 f[i], 如果先转移完, 在取跟新 f[i], 那必定超时(每遇到一个 '1' 串, 你都要重新遍历一遍)
所以每个位置存两个 f[i] 就好了, 当前的和上一个的, 通过下标判断, 应该取哪个和更新哪个
这样 f[i] 我们也有了
总的复杂度 O(n), 常数可能有点大, 并查集这里直接是O(1)
#include <bits/stdc++.h>
#define all(n) (n).begin(), (n).end()
#define se second
#define fi first
#define pb push_back
#define mp make_pair
#define sqr(n) (n)*(n)
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
typedef pair<ll, ll> PLL;
typedef pair<ll, int> PLI;
typedef vector<int> VI;
typedef double db;
template<class T1, class T2> bool umin(T1& a, T2 b) { return a > b ? (a = b, true) : false; }
template<class T1, class T2> bool umax(T1& a, T2 b) { return a < b ? (a = b, true) : false; }
template<class T> void clear(T& a) { T().swap(a); }
const int N = 5e5 + 5;
int n, m, _, k;
int fa[N], siz[N];
ll g[N], t[N];
PII f[N];
char s[N];
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void unit(int x, int y) {
x = find(x); fa[y] = x; siz[x] += siz[y];
}
int get(int x, int k) {
if (f[k] == PII{ 0, 0 }) return 0;
if (f[k].se > f[k].fi) swap(f[k].fi, f[k].se);
if (fa[f[k].fi] != fa[x]) return f[k].fi;
return f[k].se;
}
void change(int x, int k) {
if (f[k].fi == 0) f[k].fi = x;
else if (f[k].se == 0) f[k].se = x;
else if (f[k].fi > f[k].se) f[k].se = x;
else f[k].fi = x;
}
int main() {
IOS; cin >> n >> s + 1;
rep(i, 1, n) fa[i] = i;
rep(i, 1, n)
if (s[i] == '1') {
t[i] = t[i - 1] + 1; siz[i] = 1;
if (s[i - 1] == '1') unit(i - 1, i);
}
rep(i, 1, n) {
if (s[i] == '0') g[i] = g[i - 1];
else {
g[i] = g[i - 1] + i - t[i] - get(i, t[i]) + t[i];
int a = siz[find(i)]; change(i, a - t[i] + 1);
}
}
ll ans = 0;
rep(i, 1, n) ans += g[i];
cout << ans;
return 0;
}