Codeforces Round 935 (Div. 3) A-G
A
先考虑无解情况,外在人的数量如果%3之后还剩下x人,只能靠第三类综合性人y来补充进去,如果x+y小于3则无解,有解只需要向上取整即可。
#include <bits/stdc++.h>
using ll = long long;
typedef std::pair<int, int> PII;
typedef std::array<int, 4> ay;
const int N = 2e5 + 10, M = N << 1;
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f;
const int MOD = 1e9 + 7;
#define ls u << 1
#define rs u << 1 | 1
int n, m;
inline void solve() {
int a, b, c;
std::cin >> a >> b >> c;
if (b % 3 && c < (3 - b % 3)) std::cout << -1 << '\n';
else std::cout << a + (b + c + 2) / 3 << '\n';
}
signed main(void) {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int _ = 1;
std::cin >> _;
while (_ --)
solve();
return 0;
}
B
任何两个数字都存在最小公倍数,也就是a和b烟花总有一刻会同时上升,从此时之后的m开始统计一定会是最优。基于上述思路直接输出答案即可。
#include <bits/stdc++.h>
using ll = long long;
typedef std::pair<int, int> PII;
typedef std::array<int, 4> ay;
const int N = 2e5 + 10, M = N << 1;
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f;
const int MOD = 1e9 + 7;
#define ls u << 1
#define rs u << 1 | 1
int n, m;
inline void solve() {
ll a, b, m;
std::cin >> a >> b >> m;
std::cout << (m + a) / a + (m + b) / b << '\n';
}
signed main(void) {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int _ = 1;
std::cin >> _;
while (_ --)
solve();
return 0;
}
C
前缀和统计0和1的数量,然后枚举车的位置即可。需要注意的是题目中的村庄中央是直接n/2,也就是说当n为3的时候村庄中央不是2而是1.5。
#include <bits/stdc++.h>
using ll = long long;
typedef std::pair<int, int> PII;
typedef std::array<int, 4> ay;
const int N = 3e5 + 10, M = N << 1;
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f;
const int MOD = 1e9 + 7;
#define ls u << 1
#define rs u << 1 | 1
int n, m;
inline void solve() {
std::cin >> n;
std::string str;
std::cin >> str;
str = " " + str;
std::vector<int> num0(n + 1), num1(n + 1);
for (int i = 1; i <= n; i ++) {
if (str[i] == '0') num0[i] ++;
else num1[i] ++;
num0[i] += num0[i - 1];
num1[i] += num1[i - 1];
}
int pos = -1;
if (num1[n] >= (n + 1) / 2) pos = 0;
for (int i = 1; i <= n; i ++) {
int suml = num0[i];
int sumr = num1[n] - num1[i];
if (suml >= (i + 1) / 2 && sumr >= (n - i + 1) / 2) {
if (pos == -1) pos = i;
else {
double disi = std::abs(double(n) / 2 - i);
double dispos = std::abs(double(n) / 2 - pos);
if (disi < dispos) pos = i;
}
}
}
std::cout << pos << '\n';
}
signed main(void) {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int _ = 1;
std::cin >> _;
while (_ --)
solve();
return 0;
}
D
传送门
当i > m 时 如果想跟第i个人换位是不影响第i+1个人的决策的,也就是说可以先和第i+1个人换位再换到i也可以直接换到i。以此类推大于m时直接取ai和bi的最小值即可。
最终目标是要换进前m个人里面,此时只能跟其中一个人交换,枚举这个人i是谁并且支付i + 1 —— m之间人的b即可。
#include <bits/stdc++.h>
using ll = long long;
typedef std::pair<int, int> PII;
typedef std::array<int, 4> ay;
const int N = 2e5 + 10, M = N << 1;
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f;
const int MOD = 1e9 + 7;
#define ls u << 1
#define rs u << 1 | 1
int n, m;
ll a[N], b[N];
inline void solve() {
std::cin >> n >> m;
for (int i = 1; i <= n; i ++) std::cin >> a[i];
for (int i = 1; i <= n; i ++) std::cin >> b[i];
ll sum = 0;
ll mx = inf;
for (int i = m + 1; i <= n; i ++) sum += std::min(a[i], b[i]);
for (int i = n - 1; i; i --) b[i] += b[i + 1];
b[n + 1] = 0;
for (int i = 1; i <= m; i ++) {
mx = std::min(a[i] + b[i + 1] - b[m + 1] + sum, mx);
}
std::cout << mx << '\n';
}
signed main(void) {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int _ = 1;
std::cin >> _;
while (_ --)
solve();
return 0;
}
E
观察发现整个过程中m一定不会等于1,因为只有当L=1,R=2时(L+R)/2才会等于1,但是R-L==1时会终止,所以把x换到位置1能保证x不不出现在二分的过程中。然后模拟一遍二分的过程找出最终的L,让1和L交换位置即可。整个过程刚好两次交换。
#include <bits/stdc++.h>
using ll = long long;
typedef std::pair<int, int> PII;
typedef std::array<int, 4> ay;
const int N = 2e5 + 10, M = N << 1;
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f;
const int MOD = 1e9 + 7;
#define ls u << 1
#define rs u << 1 | 1
int n, m;
int a[N], pos[N];
inline void solve() {
std::cin >> n >> m;
for (int i = 1; i <= n; i ++) {
std::cin >> a[i];
pos[a[i]] = i;
}
std::cout << 2 << '\n';
int l = 1, r = n + 1;
int mid = l + r >> 1;
std::cout << 1 << ' ' << pos[m] << '\n';
std::swap(a[1], a[pos[m]]);
while (r - l != 1) {
mid = l + r >> 1;
if (a[mid] <= m) l = mid;
else r = mid;
}
std::cout << l << ' ' << 1 << '\n';
}
signed main(void) {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int _ = 1;
std::cin >> _;
while (_ --)
solve();
return 0;
}
F
传送门
权值线段树上二分板子题,不想研究其他写法。
#include <bits/stdc++.h>
using ll = long long;
typedef std::pair<int, int> PII;
typedef std::array<int, 4> ay;
const int N = 2e5 + 10, M = N << 1;
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f;
const int MOD = 1e9 + 7;
int n, m;
PII a[N];
int num[N * 30];
int L[N * 30], R[N * 30], idx;
#define ls L[u]
#define rs R[u]
inline void insert(int u, int l, int r, int v, int x) {
if (l == r) {
num[u] += x;
return ;
}
int mid = l + r >> 1;
if (v <= mid) {
if (!ls) ls = ++ idx;
insert(ls, l, mid, v, x);
} else {
if (!rs) rs = ++ idx;
insert(rs, mid + 1, r, v, x);
}
num[u] = num[ls] + num[rs];
}
inline void clear(int u) {
if (ls) clear(ls);
if (rs) clear(rs);
ls = rs = num[u] = 0;
}
inline int query(int u, int l, int r, int k) {
if (l == r) return l;
int mid = l + r >> 1;
if (num[rs] >= k) return query(rs, mid + 1, r, k);
return query(ls, l, mid, k - num[rs]);
}
inline void solve() {
clear(1);
std::cin >> n;
idx = 1;
for (int i = 1; i <= n; i ++) std::cin >> a[i].second;
for (int i = 1; i <= n; i ++) std::cin >> a[i].first;
for (int i = 1; i <= n; i ++) insert(1, 1, 1e9, a[i].second, 1);
ll ans = 0, sum = 0;
for (int i = 1; i <= n; i ++) {
if (num[1] < i) break;
int t = query(1, 1, 1e9, i);
if (1ll * t * i > ans) {
ans = 1ll * t * i;
sum = i;
}
insert(1, 1, 1e9, a[a[i].first].second, -1);
}
std::cout << ans << ' ' << sum << '\n';
}
signed main(void) {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
//freopen("D:\\in.txt", "r", stdin);
int _ = 1;
std::cin >> _;
while (_ --)
solve();
return 0;
}
G
传送门
不知道为什么会被放在G感觉比F都简单。
考虑已经吃饭之后回来的学生和还没有吃过饭的学生之间的关系,如果吃完饭回来继续排队的学生的优先级小于等于还没有吃过饭的学生的最大值,那么下一个打饭的一定是还没吃过饭的第一个学生。否则就是排队回来的学生里面优先级最大的。由于优先级可能相同,所以需要用一个额外的时间戳维护优先级相同的先后顺序,所以set里面存三个参数: 优先级,时间戳,编号。然后根据时间模拟一遍即可。
#include <bits/stdc++.h>
using ll = long long;
typedef std::pair<int, int> PII;
typedef std::array<int, 3> a3;
const int N = 2e5 + 10, M = N << 1;
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f;
const int MOD = 1e9 + 7;
int n, m;
inline void solve() {
std::cin >> n >> m;
std::vector<PII> p(n + 1);
std::vector<int> suffixmax(n + 2, 0);
for (int i = 1; i <= n; i ++) {
std::cin >> p[i].first >> p[i].second;
suffixmax[i] = p[i].first;
}
for (int i = n - 1; i; i --) suffixmax[i] = std::max(suffixmax[i], suffixmax[i + 1]);
std::vector<std::vector<int>> pos(m + 1, std::vector<int>());
std::set<a3> st;
int tot = 0, l = 1;
int tim = m + 1;
for (int i = 1; i <= m; i ++) {
int pe = 0;
if (l <= n) pe = l;
if (!st.empty()) {
auto cur = *prev(st.end());
if (suffixmax[l] < cur[0]) {
pe = cur[2];
st.erase(prev(st.end()));
}
}
if (pe == l) l ++;
if (i + p[pe].second <= m) pos[i + p[pe].second].push_back(pe);
if (l > n) {
std::cout << i << '\n';
return ;
}
if (pos[i].size()) std::sort(pos[i].begin(), pos[i].end(), [&](int x, int y) {
return p[x].second < p[y].second;
});
for (auto x: pos[i]) {
tim --;
st.insert({p[x].first, tim, x});
}
tim --;
}
std::cout << -1 << '\n';
}
signed main(void) {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
//freopen("D:\\in.txt", "r", stdin);
int _ = 1;
std::cin >> _;
while (_ --)
solve();
return 0;
}