A - Move Right
右移一位,直接模拟。
void solve() {
string s;
cin >> s;
s.pop_back();
s = "0" + s;
cout << s << endl;
}
B - Unique Nicknames
用 map
统计一下即可。
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
using ll = long long;
const int maxn = 2e3 + 5;
string s[maxn], t[maxn];
map<string, int> mp;
void solve() {
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> s[i] >> t[i];
mp[s[i]]++, mp[t[i]]++;
if (s[i] == t[i])
mp[s[i]]--;
}
int ans = 1;
for (int i = 1; i <= n; ++i) {
if (mp[s[i]] > 1 && mp[t[i]] > 1)
ans = 0;
}
cout << (ans ? "Yes" : "No") << endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}
C - 1 2 1 3 1 2 1
简单递归。
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
using ll = long long;
const int maxn = 2e5 + 5;
void dfs(int n) {
if (n <= 0)
return;
dfs(n - 1);
cout << n << ' ';
dfs(n - 1);
}
void solve() {
int n;
cin >> n;
dfs(n);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}
D - Cylinder
由于数很多,用一个 pair
表示一段相同的数,然后双端队列模拟两种操作。
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
using ll = long long;
const int maxn = 2e5 + 5;
void solve() {
ll n, op, x, c;
cin >> n;
deque<pair<ll,ll>> q;
for (int i = 1; i <= n; ++i) {
cin >> op;
if (op == 1) {
cin >> x >> c;
q.push_back({c, x});
} else {
cin >> c;
ll ans = 0;
while(!q.empty()) {
auto p = q.front();
q.pop_front();
if (c > p.first) {
ans += p.first * p.second;
c -= p.first;
} else {
ans += c * p.second;
p.first -= c;
q.push_front(p);
break;
}
}
cout << ans << endl;
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}
E - Max Min
比赛的时候写的 ST 表上二分,预处理出区间最大和最小值,枚举左端点,二分右端点的范围,然后计算贡献,细节有点多。
更好的做法是用类似状压 dp 的方式统计以每个位置作为右端点产生的贡献,这样复杂度是 O ( n ) O(n) O(n) 的,并且更好写。
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
using ll = long long;
const int logn = 20;
const int maxn = 2e5 + 5;
int a[maxn];
int mx[maxn][logn], mn[maxn][logn], lg2[maxn + 1];
void init(int n) {
lg2[1] = 0;
for (int i = 2; i <= n; i++)
lg2[i] = lg2[i / 2] + 1;
}
void work(int n, int *a) {
for (int i = 1; i <= n; ++i) {
mx[i][0] = a[i];
mn[i][0] = a[i];
}
for (int j = 1; j <= logn; j++)
for (int i = 1; i + (1 << j) - 1 <= n; i++) {
mx[i][j] = max(mx[i][j - 1], mx[i + (1 << (j - 1))][j - 1]);
mn[i][j] = min(mn[i][j - 1], mn[i + (1 << (j - 1))][j - 1]);
}
}
int qmax(int &l, int &r) {
int k = lg2[r - l + 1];
return max(mx[l][k], mx[r - (1 << k) + 1][k]);
}
int qmin(int &l, int &r) {
int k = lg2[r - l + 1];
return min(mn[l][k], mn[r - (1 << k) + 1][k]);
}
void solve() {
int n, x, y;
cin >> n >> x >> y;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
init(n);
work(n, a);
ll ans = 0;
for (int i = 1; i <= n; ++i) {
int l = i, r = n, L = i - 1, R = n + 1;
while (l <= r) {
int mid = (l + r) >> 1;
if (qmax(i, mid) < x || qmin(i, mid) > y) {
L = mid, l = mid + 1;
} else {
r = mid - 1;
}
}
l = i, r = n;
while (l <= r) {
int mid = (l + r) >> 1;
if (qmax(i, mid) > x || qmin(i, mid) < y) {
R = mid, r = mid - 1;
} else {
l = mid + 1;
}
}
if (L + 1 < R) {
ans += R - L - 1;
}
}
cout << ans << endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}
F - Cards
如果对 ( a i , b i ) (a_i,b_i) (ai,bi) 连边,容易发现最后会形成若干个环。显然总的方案数就是每个环的方案数之积,所以只需要搞清楚一个环的情况就好了。不妨从最简单的情况开始研究,如果是一个链,一条边上的两个点至少取一个,那么显然有 d p [ i ] = d p [ i − 1 ] + d p [ i − 2 ] dp[i]=dp[i-1]+dp[i-2] dp[i]=dp[i−1]+dp[i−2],对应最后一个取或不取。在环上取最后一个会影响两边的点,所以有 f [ i ] = d p [ i − 1 ] + d p [ i − 3 ] f[i]=dp[i-1]+dp[i-3] f[i]=dp[i−1]+dp[i−3],这就是一个环的方案数了。
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
using ll = long long;
const ll mod = 998244353;
const int maxn = 2e5 + 5;
int a[maxn], b[maxn];
ll f[maxn], g[maxn];
namespace dsu {
int fa[maxn], sz[maxn];
void init(int n) {
iota(fa, fa + n + 1, 0);
fill(sz, sz + n + 1, 1);
}
int find(int x) {
return fa[x] == x ? fa[x] : (fa[x] = find(fa[x]));
}
bool merge(int x, int y) {
x = find(x), y = find(y);
if (x == y)
return false;
if (sz[x] < sz[y])
swap(x, y);
fa[y] = x, sz[x] += sz[y];
return true;
}
}; // namespace dsu
void solve() {
int n;
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> a[i];
for (int i = 1; i <= n; ++i)
cin >> b[i];
dsu::init(n);
for (int i = 1; i <= n; ++i) {
int x = dsu::find(a[i]);
int y = dsu::find(b[i]);
dsu::merge(x, y);
}
f[1] = 2, f[2] = 3, f[3] = 5;
g[1] = 1, g[2] = 3, g[3] = 4;
for (int i = 4; i <= n; ++i) {
f[i] = (f[i - 1] + f[i - 2]) % mod;
g[i] = (f[i - 1] + f[i - 3]) % mod;
}
ll ans = 1;
for (int i = 1; i <= n; ++i) {
int fa = dsu::find(i);
if (fa == i) {
ans = (ans * g[dsu::sz[i]]) % mod;
}
}
cout << ans << endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}
G - Dream Team
经典费用流问题。超级源点 S S S 向每个 a a a 权值连流量为 1 1 1,费用为 0 0 0 的边,每个 b b b 权值向超级汇点 T T T 连流量为 1 1 1,费用为 0 0 0 的边,对于每个人 { a i , b i , c i } \lbrace a_i,b_i,c_i \rbrace {ai,bi,ci} 从 a i a_i ai 向 b i b_i bi 连流量为 1 1 1,费用为 − c i -c_i −ci 的边,跑最小费用最大流即可。
#include<bits/stdc++.h>
#include <atcoder/all>
#define pb push_back
#define endl '\n'
using namespace std;
using ll = long long;
const int maxn = 1e5 + 5;
const ll inf = 1e12;
int a[maxn], b[maxn], c[maxn];
void solve() {
int n;
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> a[i] >> b[i] >> c[i];
vector<ll> ans;
for (int k = 1; k <= 150; ++k) {
atcoder::mcf_graph<int, ll> g(305);
int s = 301, t = 302;
for (int i = 1; i <= 150; ++i) {
g.add_edge(s, i, 1, 0);
g.add_edge(i + 150, t, 1, 0);
}
for (int i = 1; i <= n; ++i) {
g.add_edge(a[i], b[i] + 150, 1, inf - c[i]);
}
auto now = g.flow(s, t, k);
if (now.first < k)
break;
else
ans.push_back(inf * k - now.second);
}
cout << ans.size() << endl;
for (auto i : ans)
cout << i << endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}
Ex - Rearranging Problem
太难了,不补了。