Codeforces Round 896 (Div. 2)
Codeforces Round 896 (Div. 2)
A. Make It Zero
分析:
如果\(n\)为偶数,那么我们可以选定整个数组操作两次,所有元素必定会为0。
如果\(n\)为奇数,那么\(n-1\)必定为偶数,先将后\(n-1\)个数变为0,然后同理选定第一个数和第二个数操作两次即可。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using i128 = __int128;
int n, m;
void solve()
{
scanf("%d", &n);
vector<int> a(n + 1);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
}
if (n & 1)
{
printf("4\n");
printf("2 %d\n", n);
printf("2 %d\n", n);
printf("1 2\n");
printf("1 2\n");
}
else
{
printf("2\n");
printf("1 %d\n", n);
printf("1 %d\n", n);
}
}
int main()
{
int t = 1;
scanf("%d", &t);
while (t--)
{
solve();
}
return 0;
}
B. 2D Traveling
分析:
两点之间直线最短。
从起点到终点,如果中途不是经过主要城市旅费免费,那么花费一定会大于直接从起点飞到终点。
所以我们遍历得到主要城市到起点和终点的直线距离,起点到达最近的主要城市的距离加上终点到达最近主要城市的距离就是我们经过主要城市的最小花费。
最后还有一种情况就是起点直达终点,二者取较小即可。
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
typedef pair<int, int> pii;
typedef pair<pair<ll, ll>, ll> piii;
#define fi first
#define se second
using i128 = __int128;
int n, m;
void solve()
{
int k, a, b;
scanf("%d %d %d %d", &n, &k, &a, &b);
vector<piii> v(n + 1);
for (int i = 1; i <= n; i++)
{
scanf("%lld %lld", &v[i].fi.fi, &v[i].fi.se);
v[i].se = i;
}
if (a <= k && b <= k)
{
printf("0\n");
return;
}
ll ans = abs(v[a].fi.fi - v[b].fi.fi) + abs(v[a].fi.se - v[b].fi.se);
vector<ll> st(k + 1), ed(k + 1);
ll res = 0;
for (int i = 1; i <= k; i++)
{
if (a > k)
{
st[i] = abs(v[a].fi.fi - v[i].fi.fi) + abs(v[a].fi.se - v[i].fi.se);
}
if (b > k)
{
ed[i] = abs(v[i].fi.fi - v[b].fi.fi) + abs(v[i].fi.se - v[b].fi.se);
}
}
sort(st.begin() + 1, st.end());
sort(ed.begin() + 1, ed.end());
if (k)
{
res = st[1] + ed[1];
ans = min(ans, res);
}
printf("%lld\n", ans);
}
int main()
{
int t = 1;
scanf("%d", &t);
while (t--)
{
solve();
}
return 0;
}
C. Fill in the Matrix
分析:
根据题意,除非没得选择,否则\(0\)尽量不要不要出现在第一列。
同理,\(i\)尽量不要出现在第\(i\)列。
同时,我们要\(mex(c[i])\)尽量大,那么\(i\)尽量出现到非\(i\)列。
那么我们可以进行构造,第一行排列 n 0 1 2 3 4 … ,第二行 n-1 n 0 1 2 …。
即每行都是正常升序排列,\(0\)开头的位置为下标\(2\)到\(n\)(这里总列数下标为1到n),依次循环。
通过观察,我们能发现
若 \(n <= m\) : 矩阵M的最大 \(mex\) 为 \(n + 1\)。
若 \(n > m\): 矩阵M的最大 \(mex\) 为 $ m 。$
特别的,当\(m == 1\) 时: 矩阵M的最大 \(mex\) 为 \(0\)。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
typedef pair<int, int> pii;
typedef pair<pair<ll, ll>, ll> piii;
#define fi first
#define se second
using i128 = __int128;
int n, m;
void solve()
{
scanf("%d %d", &n, &m);
vector<vector<int>> g(n + 10, vector<int>(m + 10));
int cur = 1;
for (int i = 1; i <= n; i++)
{
cur++;
if (((cur - 1) % m + m) % m + 1 == 1)
{
cur++;
}
int k = 0;
for (int j = cur; j < cur + m; j++)
{
int idx = ((j - 1) % m + m) % m + 1;
g[i][idx] = k++;
}
}
ll ans = 0;
if (n == m)
{
ans = n;
}
else if (n < m)
{
ans = n + 1;
}
else
{
ans = m;
}
if (m == 1)
{
ans = 0;
}
printf("%d\n", ans);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
printf("%d ", g[i][j]);
}
printf("\n");
}
}
int main()
{
int t = 1;
scanf("%d", &t);
while (t--)
{
solve();
}
return 0;
}
D1. Candy Party (Easy Version)
分析:
首先,我们设 \(sum = \sum_{i = 1}^na_i\)。
根据题意可知,设\(avg = {\frac {sum} n}\),若avg非整数,那么必然无解。
若avg为整数即\(n | sum\),那么我们继续。
所有人都要那一次糖果和给一次糖果,每次糖果变化都是\(2^x\)个。
所以,对于每一个人都存在
这里的\(x\)和\(y\)都是唯一的。
因此我们可以处理出所有人需要得到的\(2^x\)和需要给出的\(2^y\),然后判断得到和给出是否平衡即可。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
typedef pair<int, int> pii;
typedef pair<pair<ll, ll>, ll> piii;
#define fi first
#define se second
using i128 = __int128;
int n, m;
int lowbit(int x)
{
return x & -x;
}
void solve()
{
scanf("%d", &n);
vector<int> a(n + 1);
ll s = 0;
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
s += a[i];
}
if (s % n)
{
printf("NO\n");
return;
}
ll avg = s / n;
vector<int> out(40), in(40);
unordered_map<int, pii> q;
for (int i = 0; i <= 30; i++)
{
for (int j = 0; j < i; j++)
{
int res = (1 << i) - (1 << j);
q[res] = {i, j};
}
}
for (int i = 1; i <= n; i++)
{
if (a[i] > avg)
{
int d = a[i] - avg;
if (!q.count(d))
{
printf("NO\n");
return;
}
out[q[d].fi]++;
in[q[d].se]++;
}
else if (a[i] < avg)
{
int d = avg - a[i];
if (!q.count(d))
{
printf("NO\n");
return;
}
out[q[d].se]++;
in[q[d].fi]++;
}
}
if (out == in)
{
puts("YES");
}
else
{
puts("NO");
}
// puts("YES");
}
int main()
{
int t = 1;
scanf("%d", &t);
while (t--)
{
solve();
}
return 0;
}
D2.Candy Party (Hard Version)
分析:
本题与上一题区别在于,原本是每个人必须拿一次糖果并且给一次糖果,这里我们拿和给的次数都是小于等于1。
那么,在处理的时候,我们设\(d_i = a_i - avg\)。当\(d_i\)为\(2^k\)的时候,我们不必在对其进行拿取的二次拆分,只需一次拿或取即可。
同时,当\(d_i\)为\(2^k\)的时候,我们还可以进行灵活变换。
即如果两步操作的拿取不匹配时,我们可以将这些一步操作,拆分成两步来填补空缺(一步操作的拿取单独记录)。
举例:
我们需要\(2^3\)一次,那么如果单次拿取中存在\(+2^2\),我们可以拆成\(+2^3 - 2^2\)。如此拆分,对应修改加减即可。最终仍然是判断拿取数量是否平衡。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
typedef pair<int, int> pii;
typedef pair<pair<ll, ll>, ll> piii;
#define fi first
#define se second
using i128 = __int128;
int n, m;
int cnt = 0;
int lowbit(int x)
{
return x & -x;
}
unordered_map<int, pii> q;
unordered_map<int, int> pos;
void init()
{
for (int i = 0; i < 31; i++)
{
pos[1 << i] = i;
}
for (int i = 0; i <= 30; i++)
{
for (int j = 0; j < i; j++)
{
int res = (1 << i) - (1 << j);
q[res] = {i, j};
}
}
}
void solve()
{
scanf("%d", &n);
vector<int> a(n + 1);
ll s = 0;
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
s += a[i];
}
if (s % n)
{
printf("NO\n");
return;
}
ll avg = s / n;
// 能一步操作解决的也能两步操作解决,但只能两步操作的就只能两步操作
// 所以,我们先看两步操作的,记录下一步操作的
// 如果两步操作没法平衡,那么我们尝试一步化两步来平衡
// 如果一步能到达ave,那么一定是d = 2^x,化成两步就一定是 2^(x+1)-2^(x)的加减操作
vector<int> out(40), in(40), v(40);
for (int i = 1; i <= n; i++)
{
if (a[i] > avg)
{
int d = a[i] - avg;
if (lowbit(d) == d)
{
out[pos[d]]++;
}
else if (q.count(d))
{
v[q[d].fi]++;
v[q[d].se]--;
}
else
{
puts("NO");
return;
}
}
else if (a[i] < avg)
{
int d = avg - a[i];
if (lowbit(d) == d)
{
in[pos[d]]++;
}
else if (q.count(d))
{
v[q[d].fi]--;
v[q[d].se]++;
}
else
{
puts("NO");
return;
}
}
}
// 因为如果当前位不平衡,那么我们一定要通过低位来调节
// 所以这里从高位到低位来处理
for (int i = 30; i > 0; i--)
{
int cur = v[i];
// 说明不需要的太多,我们要用in来调节
if (cur > 0)
{
int t = min(in[i - 1], cur);
v[i] -= t;
// 此时我们将in[i-1]的单次操作进行了划分
// 划分出的每一步不在具有划分能力
// 所以直接全部加到v[i-1]中
// 这一步之后,in[i-1]不会在需要进行转换
// 所以,我们可以顺势将in[i-1]和out[i-1]全部看成单次操作加入v[i-1];
in[i - 1] -= t;
v[i - 1] += t - in[i - 1] + out[i - 1];
}
// 这里等于0的时候,我们可以上面的说法,却in[i-1]和out[i-1]一定不用变化了
// 直接将他们单次操作的权值加入到v[i-1]即可,如果这里不对等于0操作
// 一定也要在别的地方处理
else
{
int t = min(out[i - 1], abs(cur));
v[i] += t;
out[i - 1] -= t;
v[i - 1] = (v[i - 1] - t) + out[i - 1] - in[i - 1];
}
}
for (int i = 0; i < 32; i++)
{
if (v[i])
{
puts("NO");
return;
}
}
puts("YES");
}
int main()
{
int t = 1;
init();
scanf("%d", &t);
while (t--)
{
solve();
}
return 0;
}