Codeforces Round 930 (Div. 2)
Codeforces Round 930 (Div. 2)
A - Shuffle Party
解题思路:
\(1\)会按着\(2\)的整数次幂往后跳。
代码:
#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;
using piii = pair<ll, pair<ll, ll>>;
void solve()
{
int n;
cin >> n;
ll cur = 1;
while (cur * 2 <= n)
{
cur *= 2;
}
cout << cur << endl;
}
int main()
{
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
B - Binary Path
解题思路:
最多有\(n\)种相同的解。
串的区别在于何时从第一行走向第二行。
如果右侧字符小于下面字符,一定向右走,这个位置记为\(l\)。如果右侧字符大于下面字符,此时一定得向下走,这个位置记为\(r\)。
我们在\([l + 1, r]\)之间随时都可以向下走,得到的都是最小字典序字符串,\(ans = r - (l + 1) +1\)。
代码:
#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;
using piii = pair<ll, pair<ll, ll>>;
void solve()
{
int n;
cin >> n;
string a, b;
cin >> a >> b;
a = ' ' + a;
b = ' ' + b;
string ans;
ll cnt = 0;
int idx = n;
int last = 0;
for (int i = 1; i < n; i++)
{
if (b[i] < a[i + 1])
{
idx = i;
break;
}
if (b[i] > a[i + 1])
{
last = i;
}
}
if (idx == n && last == 0)
{
ans = a + b.back();
cout << ans << endl;
cout << n << endl;
}
else
{
ans = a.substr(1, idx) + b.substr(idx);
cout << ans << endl;
if (last == 0)
{
cout << idx << endl;
}
else
{
cout << idx - last << endl;
}
}
}
int main()
{
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
C - Bitwise Operation Wizard
解题思路:
首先,\(a_i |a _i = a_i\),我们可以询问\(n - 1\)次找到\(n - 1\)的下标\(pos\)。
然后,找出按位或\(a_{pos}\)得到最大值的数,取这些数中的最小值位置\(j\)。因为最小值二进制中\(1\)的位置一定刚好填补\(a_{pos}\)中\(0\)的位置。
按位异或最大值两位置即为\((pos, j)\)。
代码:
#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;
using piii = pair<ll, pair<ll, ll>>;
int lowbit(int x)
{
return x & -x;
}
void ask(int a, int b, int c, int d)
{
cout << '?' << ' ' << a << ' ' << b << ' ' << c << ' ' << d << "\n";
cout.flush();
}
void solve()
{
int n;
cin >> n;
int a = 0;
int b = 0;
int c = 1;
int d = 1;
int mx = 0;
for (int i = 1; i < n; i++)
{
c = d = i;
ask(a, b, c, d);
char ch;
cin >> ch;
if (ch == '<')
{
a = b = c;
}
}
mx = a;
int cur = 0;
for (int i = 1; i < n; i++)
{
ask(mx, cur, mx, i);
char ch;
cin >> ch;
if (ch == '<')
{
cur = i;
}
else if (ch == '=')
{
ask(cur, cur, i, i);
cin >> ch;
if (ch == '>')
{
cur = i;
}
}
}
cout << '!' << ' ' << mx << ' ' << cur << endl;
}
int main()
{
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
D - Pinball
解题思路:
对案例模拟一下可以发现,对于位置\(i\),最终是从左出去还是从右边出去取决于,自身的符号,左侧\(>\)的数量以及,右侧\(<\)的数量。
举例:\((1, 2, 3, 4, 5):>><<<\)
对于位置\(3\)而言,(左侧\(>\)数量) 小于等于 (右侧\(<\)数量),最终从左边出去。
对于答案的计算,下标设位置分别为\((p_1,p_2,i, p_3, p_5)\):
\[\begin{align*}
&(i - p_2 + 1) + (p_3 - p_2) + (p_3 - p_1) + (p_4 - p_1) + (n - p_1) \\
= &2 \times[(p_3 + p_4) - (p_1 + _2)] + n + i + 1
\end{align*}
\]
根据公式,不难发现,前后缀规律,具体位置可用二分查找到,其余情况也这样讨论即可。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
typedef double db;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;
void solve()
{
int n;
cin >> n;
string s;
cin >> s;
s = ' ' + s;
// prel[i]: i左边 < 的数量
// prer[i]: i左边 > 的数量
vector<int> prel(n + 1), prer(n + 1);
for (int i = 1; i <= n; i++)
{
prel[i] = prel[i - 1];
prer[i] = prer[i - 1];
if (s[i] == '<')
{
prel[i]++;
}
else
{
prer[i]++;
}
}
vector<ll> pre(n + 10), suf(n + 10);
for (int i = 1; i <= n; i++)
{
pre[i] = pre[i - 1];
suf[i] = suf[i - 1];
if (s[i] == '>')
{
pre[i] += i;
}
else
{
suf[i] += i;
}
}
for (int i = 1; i <= n; i++)
{
int lc = prer[i - 1];
int rc = prel[n] - prel[i];
if (s[i] == '<')
{
// 最终从左边出去
if (lc <= rc)
{
if (lc == 0)
{
cout << i << ' ';
continue;
}
else
{
int l = i;
int r = n + 1;
while (l + 1 < r)
{
int mid = l + r >> 1;
if (prel[mid] - prel[i] >= lc)
{
r = mid;
}
else
{
l = mid;
}
}
ll ans = 2 * (suf[r] - suf[i] - (pre[i - 1])) + i;
cout << ans << ' ';
}
}
// 最终从右边出去
else
{
int l = 0;
int r = i;
while (l + 1 < r)
{
int mid = l + r >> 1;
if (prer[i - 1] - prer[mid] >= rc + 1)
{
l = mid;
}
else
{
r = mid;
}
}
ll ans = 2 * (suf[n] - suf[i] - (pre[i - 1] - pre[l])) + n + i + 1;
cout << ans << ' ';
}
}
else
{
// 最终从右边出去
if (rc <= lc)
{
if (rc == 0)
{
cout << n - i + 1 << ' ';
continue;
}
else
{
int l = 0;
int r = i;
while (l + 1 < r)
{
int mid = l + r >> 1;
if (prer[i - 1] - prer[mid] >= rc)
{
l = mid;
}
else
{
r = mid;
}
}
ll ans = 2 * (suf[n] - suf[i] - (pre[i - 1] - pre[l])) + n - i + 1;
cout << ans << ' ';
}
}
// 最终从左边出去
else
{
int l = i;
int r = n + 1;
while (l + 1 < r)
{
int mid = l + r >> 1;
if (prel[mid] - prel[i] > lc)
{
r = mid;
}
else
{
l = mid;
}
}
ll ans = 2 * (suf[r] - suf[i] - (pre[i - 1])) - i;
cout << ans << ' ';
}
}
}
cout << endl;
}
int main()
{
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
E - Pokémon Arena
(推荐讲解视频):
虚点优化建图,跑最短路。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
typedef double db;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;
const ll inf = 1ll << 60;
void solve()
{
int n, m;
cin >> n >> m;
vector<ll> c(n + 1);
for (int i = 1; i <= n; i++)
{
cin >> c[i];
}
vector<vector<pii>> a(m + 10, vector<pii>()), e(3 * m * n + 10, vector<pii>());
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
int x;
cin >> x;
a[j].emplace_back((pii){x, i});
}
}
int idx = n + 1;
// 对于每个点i,对于每个属性分别建立两个虚点
// 一个用来增加属性值挑战强者
// 一个用来欺负弱者
for (int j = 1; j <= m; j++)
{
sort(a[j].begin(), a[j].end());
// 对i建通往属性j值的边,数值大小不变,只出出场费c[i]
for (int i = 0; i < n; i++)
{
int id = a[j][i].se;
e[id].push_back({idx, c[id]});
// 建一条战胜强者的边,花费就是二者属性值之差
if (i != n - 1)
{
ll cost = a[j][i + 1].fi - a[j][i].fi;
e[idx].push_back({idx + 1, cost});
}
// 从属性本值点,进入保守通道,该通道不在会增加属性值,只会有高属性值连向低属性值
e[idx].push_back({idx + n, 0});
idx++;
}
for (int i = 0; i < n; i++)
{
int id = a[j][i].se;
// 能到达保守位,说明具备击败该点的实力,从该点的保守位去击败该点
e[idx].push_back({id, 0});
// 在保守区域,建一条击败弱者的边,不需要增加属性值,无多余花费
if (i != n - 1)
{
e[idx + 1].push_back({idx, 0});
}
idx++;
}
}
// 跑一条从n到1的最短路即可。
vector<ll> dist(3 * m * n + 10, inf);
dist[n] = 0;
priority_queue<pii, vector<pii>, greater<pii>> q;
q.push({dist[n], n});
vector<bool> vis(3 * n * m + 10, false);
while (q.size())
{
auto t = q.top();
q.pop();
int u = t.se;
if (vis[u])
{
continue;
}
vis[u] = true;
for (auto x : e[u])
{
int v = x.fi;
ll w = x.se;
if (dist[v] > dist[u] + w)
{
dist[v] = dist[u] + w;
q.push({dist[v], v});
}
}
}
cout << dist[1] << endl;
}
int main()
{
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}

浙公网安备 33010602011771号