Codeforces Round 1050 (Div. 4)
https://codeforces.com/contest/2148
A. Sublime Sequence
题意:给定一个整数 x 和 n,要求得到一个长度为 n 的序列,形如 x, -x, x, -x...... 求序列之和。
题解:长度为奇数时输出 x,长度为偶数时输出 0。
void slove()
{
int x, n; cin >> x >> n;
if (n & 1) cout << x << endl;
else cout << 0 << endl;
}
B. Lasers
题意:在一个二维平面,需要从 (0, 0) 到 (x, y)。水平方向有 n 个激光束,垂直方向有 m 个激光束。给出这些激光束在水平方向和垂直方向的截距。问需要穿过多少个激光束才能到达 (x, y)。约束:每一个截距都小于 x 和 y。
题解:输出 \(n + m\)
void slove()
{
int n, m; cin >> n >> m;
int x, y; cin >> x >> y;
for (int i = 1; i <= n; i++) {
int a; cin >> a;
}
for (int i = 1; i <= m; i++) {
int a; cin >> a;
}
cout << n + m << endl;
}
C. Pacer
题意:花费一个单位时间从体育馆的一边到另一边,用1/0表示两边,初始在0侧。每次到另一侧可以加 1 分,现在要求在 t 时刻在 x 处。满足这些条件的前提下的最大得分。
题解:在两个时刻之间运动,时间之差为 c,开始运动的位置处于 y,要求处于的位置为 pos。
c if (pos = y and c is even) or (pos != y and c is odd)
c - 1 if (pos = y and c is odd) or (pos != y and c is even)
void slove()
{
int n, m; cin >> n >> m;
vector<PII> a(n + 5);
for (int i = 1; i <= n; i++) {
int x, y; cin >> x >> y;
a[i] = {x, y};
}
int res = 0;
int t = 0, pos = 0;
for (int i = 1; i <= n; i++) {
int x = a[i]._1, y = a[i]._2;
if (pos == y) {
int c = x - t;
if (c & 1) res += c - 1;
else res += c;
} else {
int c = x - t;
if (c & 1) res += c;
else res += c - 1;
}
t = x;
pos = y;
}
res += m - t; // 最后还要跑完全部的时间
cout << res << endl;
}
D. Destruction of the Dandelion Fields
题意:一个收割机收割草坪,如果草坪为奇数开关收割机,开变关,关变开。每块草坪只可以收割一次,可以任意选择收割顺序,问可最大的草坪面积为多少。初始收割机为关闭。
题解:特判没有奇数,总贡献为0。存在奇数,累加所有偶数。将奇数排序,第一个收割最大的,选一个最小的切换状态,在选次大的......
void slove()
{
int n; cin >> n;
vector<int> a(n + 5);
vector<int> b;
bool flg = false;
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (a[i] & 1) {
b.emplace_back(a[i]);
flg = true;
}
}
if (!flg) {
cout << 0 << endl;
return;
}
LL res = 0;
for (int i = 1; i <= n; i++) {
if ((a[i] & 1) == 0) res += a[i];
}
sort(b.begin(), b.end());
int m = b.size();
for (int i = 0, j = m - 1; i <= j; i++, j--) {
res += b[j];
}
cout << res << endl;
}
E. Split
题意:有k个集合,选择一个子数组,将这个子数组的元素加入到集合1中,其它元素可以添加进任意集合中。是否可以是的所有集合的元素个数相同。这样的子数组有多少个。
题解:对于任意一个元素来说,元素个数一定可以被k整除,否则不可能使得所有的集合元素个数相同。
对任意一个元素 x 来说,集合1中最大的数量为 \(\frac{cnt_x}{k}\) 。所以子数组中,每个元素的个数不能超过这个数量,即可满足条件。
使用滑动窗口,固定子数组的一个端点,移动即可。每一个满足条件的窗口,贡献为整个窗口的大小。
void slove()
{
int n, k; cin >> n >> k;
vector<int> a(n + 5), cnt(n + 5);
for (int i = 1; i <= n; i++) {
cin >> a[i];
cnt[a[i]]++;
}
for (int i = 1; i <= n; i++) {
if (cnt[i] % k != 0) {
cout << "0" << endl;
return;
}
cnt[i] /= k;
}
vector<int> t(n + 5);
LL res = 0;
for (int i = 1, j = 1; j <= n && j >= i; j++) {
t[a[j]]++;
while (t[a[j]] > cnt[a[j]]) {
t[a[i]]--;
i++;
}
res += j - i + 1;
}
cout << res << endl;
}
F. Gravity Falls
题意:有 n 行元素,每行元素个数任意。现在可以将这 n 行元素任意交换顺序,下面的元素个数小于上面的元素个数,重力使得上面的元素掉下来,可以得到一个字典序最小的序列。(每行元素为 k 个。所有 k 的和不超过 2e5)
题解:元素个数最多的就是最后答案的长度,枚举这个最大长度,也就是列,枚举行,得到一个最小的元素,可能会有多行都有这个最小元素。判断出这些行中字典序最小的。字典序最小的行就可以到下面。
PS:注意判断一定是字典序最小的,元素相等,长度不等,长度小的字典序更小。
1 2 3 4
1 2 3 4 5 6
9 9 9 9 1 2初始要选第一个,不然选择第二个会使得第三个没选。
const int N = 2e5 + 5;
vector<int> g[N];
bool check(int a, int b, int i) {
int len = min(g[a].size(), g[b].size());
for (; i < len; i++) {
if (g[a][i] != g[b][i]) return g[b][i] < g[a][i];
}
return g[b].size() < g[a].size();
}
void slove()
{
int n; cin >> n;
for (int i = 1; i <= n; i++) {
g[i].clear();
}
int maxv = 0;
for (int i = 1; i <= n; i++) {
int k; cin >> k;
g[i].resize(k);
for (int j = 0; j < k; j++) cin >> g[i][j];
maxv = max(maxv, k); // 得到最长的长度
}
int cnt = 0;
vector<int> res(maxv);
for (int i = 0; i < maxv; i++) { // 枚举列
vector<int> idx;
idx.reserve(n);
int minv = 1e9;
for (int j = 1; j <= n; j++) { // 枚举行,得到最小的元素
if (i >= g[j].size()) continue;
minv = min(minv, g[j][i]);
}
for (int j = 1; j <= n; j++) { // 最小元素对应的行
if (i >= g[j].size()) continue;
if (g[j][i] == minv) idx.emplace_back(j);
}
int id = idx[0];
for (int j = 1; j < idx.size(); j++) { // 枚举备选行,得到最小的行
int now = idx[j];
if (check(id, now, i)) {
id = now;
}
}
for (int j = i; j < g[id].size(); j++) { // 将这一行的元素加入答案
res[cnt++] = g[id][j];
}
i = g[id].size() - 1; // 在重新得到新的行
}
for (int i = 0; i < cnt; i++) cout << res[i] << " ";
cout << endl;
}
G. Farmer John's Last Wish
题意:给定一个数组,求前缀[1, n], gcd(1~k) > gcd(1~k+1)的最大的k。\(1\leq k\leq n-1\) 。前缀可以任意排列。
题解:对于任意序列,设 g = gcd(1~k), x 为任意元素。始终满足 \(g \geq gcd(g, x)\) 。现在需要的就是去掉等号。
g一定是序列的某一个因子,如果某个因子的数量不等于序列的长度,表示至少有一个元素没有这个因子,再加上这个元素的gcd一定不会等于。最大的位置就是这个因子的数量。
const int N = 2e5 + 5;
vector<int> divi[N];
void init() {
for (int i = 1; i < N; i++) {
for (int j = i; j < N; j += i) {
divi[j].emplace_back(i);
}
}
}
void slove()
{
int n; cin >> n;
vector<int> a(n + 5);
for (int i = 1; i <= n; i++) cin >> a[i];
vector<int> cnt(n + 5);
int res = 0, g = 0;
for (int i = 1; i <= n; i++) {
int x = a[i];
for (int v : divi[x]) {
cnt[v]++;
if (cnt[v] != i) {
res = max(res, cnt[v]);
}
}
if (gcd(g, x) != g) { // 可能x中没有对应的因子,在枚举的时候就不会包含所有的前面元素的因子集。
res = max(res, cnt[g]);
}
g = gcd(g, x);
cout << res << " ";
}
cout << endl;
}
浙公网安备 33010602011771号