Educational Codeforces Round 161 (Rated for Div. 2)
Educational Codeforces Round 161 (Rated for Div. 2)
A - Tricky Template
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
void solve()
{
int n;
cin >> n;
string a, b, c;
cin >> a >> b >> c;
for (int i = 0; i < n; i++)
{
if (a[i] == b[i] && c[i] != a[i])
{
puts("YES");
return;
}
else if (a[i] != b[i] && c[i] != a[i] && c[i] != b[i])
{
puts("YES");
return;
}
}
puts("NO");
}
int main()
{
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
B - Forming Triangles
解题思路:
要么三根一样长,要么两根一样长。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
void solve()
{
int n;
cin >> n;
vector<ll> a(n + 1);
map<ll, ll> cnt;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
cnt[a[i]]++;
}
ll ans = 0;
ll cur = 0;
for (int i = 0; i <= n; i++)
{
ans += (cnt[i] * (cnt[i] - 1) * (cnt[i] - 2)) / 6;
ans += (cnt[i] * (cnt[i] - 1)) / 2 * cur;
cur += cnt[i];
}
cout << ans << endl;
}
int main()
{
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
C - Closest Cities
解题思路:
求花费前缀和、后缀和,分别代表两个方向。
如果是最近就花费一,否则花费距离差。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
void solve()
{
int n;
cin >> n;
vector<ll> a(n + 1);
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
vector<ll> pre(n + 1), suf(n + 1);
pre[1] = 0;
suf[n] = a[n];
for (int i = 2; i <= n; i++)
{
if (i == 2)
{
pre[i] = pre[i - 1] + 1;
}
else if (i == n)
{
if (a[n - 1] - a[n - 2] > a[n] - a[n - 1])
{
pre[i] = pre[i - 1] + 1;
}
else
{
pre[i] = pre[i - 1] + a[n] - a[n - 1];
}
}
else
{
if (a[i - 1] - a[i - 2] > a[i] - a[i - 1])
{
// cout << i << endl;
pre[i] = pre[i - 1] + 1;
}
else
{
pre[i] = pre[i - 1] + a[i] - a[i - 1];
}
}
}
for (int i = n - 1; i; i--)
{
if (i == n - 1)
{
suf[i] = suf[i + 1] + 1;
}
else if (i == 1)
{
if (a[3] - a[2] > a[2] - a[1])
{
suf[i] = suf[i + 1] + 1;
}
else
{
suf[i] = suf[i + 1] + a[2] - a[1];
}
}
else
{
if (abs(a[i + 1] - a[i + 2]) > abs(a[i] - a[i + 1]))
{
suf[i] = suf[i + 1] + 1;
}
else
{
suf[i] = suf[i + 1] + a[i + 1] - a[i];
}
}
}
// cout << pre[4] << endl;
int m;
cin >> m;
while (m--)
{
int x, y;
cin >> x >> y;
if (x < y)
{
cout << pre[y] - pre[x] << endl;
}
else
{
cout << suf[y] - suf[x] << endl;
}
}
}
int main()
{
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
E - Increasing Subsequences
解题思路:
一个长度为\(n\)单调递增的序列,他的严格单调递增子序列数量为\(2^n\)。
询问要求子序列数为\(len\)。我们确定上下界\(2^a \leq len \leq 2^{a + 1}\)。
我们考虑先构造基序列\((1,2,3,...,a)\),在其基础上添砖加瓦。
观察一下,不难发现,不同位置加入最小数,递增子序列的数量会增加不同的\(2\)的整数次幂个。
举例:
\((1,2,3,4,5):32个严格增子序列\)
\((1,2,3,4,5,1):32 + 1个严格增子序列\)
\((1,2,3,4,1,5):32 + 2个严格增子序列\)
\((1,2,3,1,4,5):32 + 4个严格增子序列\)
\((1,2,1,3,4,5):32 + 8个严格增子序列\)
由于\(2^a \leq len \leq 2^{a + 1}\),所以我们将剩下的需求二进制拆分,序列长度一定不会增加超过\(log(n)\)。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
void solve()
{
ll n;
cin >> n;
ll l = 0;
ll cur = 0;
while ((1ll << cur) <= n)
{
l = 1ll << cur;
cur++;
}
cur--;
list<int> q;
for (int i = 1; i <= cur; i++)
{
q.push_back(i);
}
ll res = n - l;
auto find = [&](int x)
{
for (auto it = q.begin(); it != q.end(); it++)
{
if (*it == x)
{
return it;
}
}
return q.end();
};
map<ll, int> mp;
for (int i = 0; i <= cur; i++)
{
mp[i] = cur - i + 1;
}
for (int i = 0; i <= cur; i++)
{
if (res >> i & 1)
{
auto it = find(mp[i]);
q.insert(it, 1);
}
// for (auto it : q)
// {
// cout << it << ' ';
// }
// cout << endl;
}
cout << q.size() << endl;
for (auto it : q)
{
cout << it << ' ';
}
cout << endl;
}
int main()
{
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
D - Berserk Monsters
解题思路:
对于第一次,我们要看所有的怪物情况。
对于之后的判断,我们只看那些左右两边有怪物更新的怪物。因为左右不变,之前存在之后一样会存在。
如果这样看的话,每次消灭已知怪物,我们就要记录他身边的两只怪物作为下一轮要看的怪物。
那么最多总共最多判断\(2 * n\)次。
我们用链表模拟,\(set\)记录即可。
时间复杂度\(O(nlogn)\)。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
void solve()
{
ll n;
cin >> n;
vector<ll> a(n + 10), b(n + 10), l(n + 10), r(n + 10);
vector<bool> die(n + 10, false);
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
for (int i = 1; i <= n; i++)
{
cin >> b[i];
l[i] = i - 1;
r[i] = i + 1;
}
// 记录需要判断是否会被击败的怪物的下标
set<int> s;
int cnt = 0;
for (int i = 1; i <= n; i++)
{
// 这里判断不受链表删除影响
if (a[i - 1] + a[i + 1] > b[i])
{
s.insert(l[i]);
s.insert(r[i]);
die[i] = true;
l[r[i]] = l[i];
r[l[i]] = r[i];
cnt++;
// cout << i << endl;
}
}
cout << cnt << ' ';
for (int i = 2; i <= n; i++)
{
cnt = 0;
// 记录被击败的怪物的下标,进行延迟更新
set<int> t;
for (auto idx : s)
{
if (die[idx] || idx == 0 || idx == n + 1)
{
continue;
}
// 这里先不进行链表删除,因为会影响判断
if (a[l[idx]] + a[r[idx]] > b[idx])
{
// cout << i << ' ' << idx << endl;
cnt++;
t.insert(idx);
}
}
// 记录下一轮需要判断是否会被击败的怪物的下标
s.clear();
// 判断完后,再进行链表删除操作,延迟更新
for (auto idx : t)
{
die[idx] = true;
s.insert(r[idx]);
s.insert(l[idx]);
l[r[idx]] = l[idx];
r[l[idx]] = r[idx];
}
cout << cnt << ' ';
}
cout << endl;
}
int main()
{
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
F - Replace on Segment
解题思路:
区间操作加\(O(n^4)\)似乎可过的复杂度,考虑区间\(dp\)。
我们对于将一个区间变成含有相同的数\(x\)有两种操作方向。
- 保留\(x\)不动,将不含\(x\)的子区间一一变为\(x\)。
- 先使得整个区间都不包含\(x\),然后一步到位。
\(f[i][j][x]:将从i到j的区间中的数全部变成x的最小操作次数\)。
\(g[i][j][x]:将从i到j的区间中的x全部去除的最小操作次数\)。
对于\(f\)的求取如上模拟:
\(f[l][r][x] = min(f[l][r][x],g[l][r][x] + 1)\)。
对于\(g\)我们发现,处理简单地将区间合并,我们使得区间所有的数都变成一个非\(x\)的数同样符合定义。
所以,我们最后可以通过\(f\)对\(g\)进行更新。
\(g[l][r][x] = min(g[l][r][x] ,f[l][r][invx])\)
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
const int N = 110;
// f[i][j][x]:将从i到j的区间中的数全部变成x的最小操作次数
int f[N][N][N];
// g[i][j][x]:将从i到j的区间中的x全部去除的最小操作次数
int g[N][N][N];
void solve()
{
int n, x;
cin >> n >> x;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
for (int i = 1; i <= n; i++)
{
for (int j = i; j <= n; j++)
{
for (int k = 1; k <= x; k++)
{
f[i][j][k] = 1e9 + 10;
g[i][j][k] = 1e9 + 10;
}
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= x; j++)
{
if (a[i] == j)
{
f[i][i][j] = 0;
g[i][i][j] = 1;
}
else
{
f[i][i][j] = 1;
g[i][i][j] = 0;
}
}
}
for (int i = n - 1; i; i--)
{
for (int j = i + 1; j <= n; j++)
{
for (int k = i; k < j; k++)
{
for (int val = 1; val <= x; val++)
{
// cout << i << ' ' << k << ' ' << k + 1 << ' ' << j << endl;
f[i][j][val] = min(f[i][j][val], f[i][k][val] + f[k + 1][j][val]);
g[i][j][val] = min(g[i][j][val], g[i][k][val] + g[k + 1][j][val]);
f[i][j][val] = min(f[i][j][val], g[i][j][val] + 1);
}
}
for (int val = 1; val <= x; val++)
{
for (int inv = 1; inv <= x; inv++)
{
if (val == inv)
{
continue;
}
g[i][j][val] = min(g[i][j][val], f[i][j][inv]);
}
}
}
}
// cout << f[1][8][3] << endl;
// cout << g[1][8][2] << endl;
int ans = 1e9 + 10;
for (int i = 1; i <= x; i++)
{
ans = min(ans, f[1][n][i]);
}
cout << ans << endl;
}
int main()
{
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}

浙公网安备 33010602011771号