CF Educational Round 51 题解
Posted libra9z
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF Educational Round 51 题解相关的知识,希望对你有一定的参考价值。
A - Vasya And Password
题意:
给你一个字符串(S),可能包含小写字母或大写字母或数字。让你改变其中的一些字符,使得改变后的字符串包含小写字母、大写字母和数字。输出改变后的字符串。保证有解。
题解:
扫描一遍,找出小写字母、大写字母和数字出现的第一个位置。然后再重新扫一遍,如果说(i)不是小写字母、大写字母或数字出现的第一个位置,就可以把(S_i)改变成没有出现过的一种字符。
Code:
// Code by H~$~C
#include <bits/stdc++.h>
using namespace std;
#define epoch_time chrono::steady_clock::now().time_since_epoch().count()
static mt19937 __gen(epoch_time);
#define random_shuffle(begin, end) shuffle(begin, end, __gen)
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
constexpr int inf = 0x3f3f3f3f;
constexpr ll lnf = 0x3f3f3f3f3f3f3f3f;
#define is_lower(a) (a >= 'a' && a <= 'z')
#define is_upper(a) (a >= 'A' && a <= 'Z')
#define is_digit(a) (a >= '0' && a <= '9')
void solve() {
string s;
cin >> s;
int n = s.length();
int pos_lower = -1, pos_upper = -1, pos_dig = -1;
for (int i = 0; i < n; ++i) {
if (is_lower(s[i])) {
pos_lower = i;
break;
}
}
for (int i = 0; i < n; ++i) {
if (is_upper(s[i])) {
pos_upper = i;
break;
}
}
for (int i = 0; i < n; ++i) {
if (is_digit(s[i])) {
pos_dig = i;
break;
}
}
for (int i = 0; i < n; ++i) {
if (i == pos_lower) continue;
if (i == pos_upper) continue;
if (i == pos_dig) continue;
if (pos_lower == -1) s[i] = 'a', pos_lower = 0;
else if (pos_upper == -1) s[i] = 'A', pos_upper = 0;
else if (pos_dig == -1) s[i] = '0', pos_dig = 0;
}
cout << s << endl;
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int tests; cin >> tests;
while (tests--) solve();
return 0;
}
B - Relatively Prime Pairs
题意:
给你一个范围([l,r],r-lmod 2=1,lle r),要你把范围里面的所有的整数分成(dfrac{r-l+1}{2})个部分,每一个部分有两个数,且这两个数互质。如果能办到,就输出YES
,然后输出方案;否则输出NO
。(1le l<rle 10^{18})
题解:
很容易发现每两个相邻的数是互质的,所以就可以把两个相邻的数分到一组,即((l,l+1),(l+2,l+3),ldots,(r-1,r))。
Code:
// Code by H~$~C
#include <bits/stdc++.h>
using namespace std;
#define epoch_time chrono::steady_clock::now().time_since_epoch().count()
static mt19937 __gen(epoch_time);
#define random_shuffle(begin, end) shuffle(begin, end, __gen)
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
constexpr int inf = 0x3f3f3f3f;
constexpr ll lnf = 0x3f3f3f3f3f3f3f3f;
signed main() {
ll l, r;
cin >> l >> r;
puts("YES");
for (ll i = l; i <= r; i += 2)
printf("%lld %lld
", i, i + 1);
return 0;
}
C - Vasya and Multisets
题意:
给你一个多重集合(S),如果(xin S)且(x)在(S)中只出现(1)次,则称(x)为好数。现在要你把(S)分成两个多重集合(A,B),使得(A,B)里面的好数的个数相等。如果办不到,输出NO
。(1le|S|le 100)
题解:
统计(S)中的好数的个数(c),如果(cmod2=0),则直接把(dfrac{c}{2})个好数分给(A),其他的所有数都分给(B)。如果(cmod 2=1),先把(dfrac{c-1}{2})个好数分给(A),其余的好数分给(B)。在剩下来的那些不是好数的数中,若存在(d)在(S)中出现的次数(>2),则可以把一个(d)分给(A),其他的(d)分给(B),这样(A,B)集合的好数就相等了。如果找不到这样的(d),那么答案就是NO
。
Code:
// Code by H~$~C
#include <bits/stdc++.h>
using namespace std;
#define epoch_time chrono::steady_clock::now().time_since_epoch().count()
static mt19937 __gen(epoch_time);
#define random_shuffle(begin, end) shuffle(begin, end, __gen)
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
constexpr int inf = 0x3f3f3f3f;
constexpr ll lnf = 0x3f3f3f3f3f3f3f3f;
int n;
int a[105];
int mp[105];
bool vis[105];
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; ++i) cin >> a[i], mp[a[i]]++;
int cnt = 0, cnt1 = 0;
for (int i = 1; i <= 100; ++i)
cnt += (mp[i] >= 3), cnt1 += (mp[i] == 1);
if (cnt == 0 && cnt1 % 2) return puts("NO") & 0;
puts("YES");
int cnt2 = 0, flag = 0;
for (int i = 1; i <= n; ++i)
if (mp[a[i]] == 1) {
++cnt2;
if (cnt2 * 2 <= cnt1) vis[i] = 0;
else vis[i] = 1;
}
else if (mp[a[i]] > 2) {
if (cnt1 % 2 && !flag)
vis[i] = 0;
else vis[i] = 1;
flag = 1;
}
else vis[i] = 'B';
for (int i = 1; i <= n; ++i)
putchar("AB"[vis[i]]);
return 0;
}
D - Bicolorings
题意:
给定一个(2 imes n)的棋盘,可以对上面的格子黑白染色,求染色后棋盘上的颜色相同联通块的个数正好为(k)的染色方案数。(1le nle 2000)
题解:
设(dp_{i,j,c_1,c_2})表示当前染色到第(i)列,前面一共有(j)个联通块,第(i-1)列的颜色分别为(c_1,c_2)。
转移的话直接枚举(c_1,c_2),是(O(1))的。总时间复杂度就是(O(nk))。
Code:
// Code by H~$~C
#include <bits/stdc++.h>
using namespace std;
#define epoch_time chrono::steady_clock::now().time_since_epoch().count()
static mt19937 __gen(epoch_time);
#define random_shuffle(begin, end) shuffle(begin, end, __gen)
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
constexpr int inf = 0x3f3f3f3f;
constexpr ll lnf = 0x3f3f3f3f3f3f3f3f;
constexpr int Maxn = 2005;
constexpr int mod = 998244353;
inline int add(int x, int y) { return (x += y) >= mod ? x - mod : x; }
inline void inc(int &x, int y) { (x += y) >= mod && (x -= mod); }
inline int mul(int x, int y) { return 1LL * x * y - 1LL * x * y / mod * mod; }
int n, k;
int dp[Maxn][Maxn][2][2];
int more(int a1, int a2, int b1, int b2) {
if (a1 == b1 && a2 == b2) return 0;
if (a1 == a2) {
if (b1 == a1 && b2 == a1) return 0;
return 1;
}
if (b1 == b2) return 0;
if (b1 == a1) return 0;
return 2;
}
signed main() {
cin >> n >> k;
dp[1][1][0][0] = 1, dp[1][1][1][1] = 1;
dp[1][2][0][1] = 1, dp[1][2][1][0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= k; ++j) {
for (int a1 = 0; a1 < 2; ++a1) {
for (int a2 = 0; a2 < 2; ++a2) {
for (int b1 = 0; b1 < 2; ++b1) {
for (int b2 = 0; b2 < 2; ++b2) {
inc(dp[i + 1][j + more(a1, a2, b1, b2)][b1][b2], dp[i][j][a1][a2]);
}
}
}
}
}
}
cout << add(add(dp[n][k][0][0], dp[n][k][0][1]), add(dp[n][k][1][0], dp[n][k][1][1])) << endl;
return 0;
}
E - Vasya and Big Integers
题意:
给你一个由数字构成的字符串(S),问你有多少种划分方式,使得划分的每段不含前导0,并且每段的数字大小在([l,r])之间。(0le lle rle 10^{10^{6}},0le Sle10^{10^{6}})
题解:
先考虑一个DP:(dp_i)表示(S_{i..|S|})这一段数字字符串有(dp_i)种划分方式满足要求。
很显然,若(S_i=0),则当(l=0)时,(dp_i=dp_{i+1}),否则(dp_i=0)。
若(S_i eq 0),则要找到最小的(L),最大的(R),使得(S_{i..(L-1)}ge l,S_{i..(R-1)}le r),那么这样(dp_i=dp_L+dp_{L+1}+dp_{L+2}+dots+dp_{R})。
但这样时间复杂度为(O(|S|^2)),显然是不行的。
首先那个求和部分可以用后缀和进行优化,现在要做的就是要快速找到(L,R)。
进一步观察可以发现,若(S_{i,i+|l|-1}ge l),则(L=i+|l|),否则(L=i+|l|+1),对于(R)也是同理。所以现在问题就转化成了判断(S_{i+|l|-1},l)和(S_{i+|r|-1},r)的大小关系。
常规的判断方法是枚举(j=0
ightarrow|l|-1),若(S_{i+j}
eq l_j),则(S_{i+|l|-1})和(l)的大小就是(S_{i+j})和(l_j)的大小。设(lcp(S_{i+|l|-1},l)=lcp),则(S_{i+|l|-1})和(l)的大小就是(S_{i+lcp})和(l_{lcp})的大小。现在问题就转化成了快速求(lcp(S_{i+|l|-1},l))。这可以通过哈希或对(l+S)建立z function
求得。
Code:
// Code by H~$~C
#include <bits/stdc++.h>
using namespace std;
#define epoch_time chrono::steady_clock::now().time_since_epoch().count()
static mt19937 __gen(epoch_time);
#define random_shuffle(begin, end) shuffle(begin, end, __gen)
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
constexpr int inf = 0x3f3f3f3f;
constexpr ll lnf = 0x3f3f3f3f3f3f3f3f;
constexpr int mod = 998244353;
inline int add(int x, int y) { return (x += y) >= mod ? x - mod : x; }
inline void inc(int &x, int y) { (x += y) >= mod && (x -= mod); }
constexpr int Maxn = 2000005;
int n, ln, rn;
char a[Maxn], l[Maxn], r[Maxn];
int zl[Maxn], zr[Maxn];
inline void buildZ(int n, const char *s, int *z) {
for (register int i = 1, l = 0, r = 0; i < n; ++i) {
if (i <= r) z[i] = (z[i - l] < r - i + 1 ? z[i - l] : r - i + 1);
while (i + z[i] < n && s[z[i]] == s[i + z[i]]) ++z[i];
if (i + z[i] - 1 > r) l = i, r = i + z[i] - 1;
}
}
inline int cmp(int len, int *z, const char *s, int pos) {
if (n - pos + 1 < len) return -1;
int x = z[pos + len + 1];
if (x == len) return 0;
return a[pos + x] < s[1 + x] ? -1 : 1;
}
int dp[Maxn], suf[Maxn];
signed main() {
scanf("%s%s%s", a + 1, l + 1, r + 1);
n = strlen(a + 1);
ln = strlen(l + 1);
rn = strlen(r + 1);
l[ln + 1] = r[rn + 1] = '$';
strcat(l + 1, a + 1);
strcat(r + 1, a + 1);
buildZ(ln + 1 + n, l + 1, zl + 1);
buildZ(rn + 1 + n, r + 1, zr + 1);
//for (int i = 1; i <= n + ln + 1; ++i) fprintf(stderr, "zl[%d] = %d
", i, zl[i]);
//for (int i = 1; i <= n + rn + 1; ++i) fprintf(stderr, "zr[%d] = %d
", i, zr[i]);
dp[n + 1] = suf[n + 1] = 1;
for (int i = n; i >= 1; --i) {
if (a[i] == '0') {
if (l[1] == '0') dp[i] = dp[i + 1];
else dp[i] = 0;
suf[i] = add(suf[i + 1], dp[i]);
continue;
}
int cmpl = cmp(ln, zl, l, i);
int cmpr = cmp(rn, zr, r, i);
int L = min(ln + i + (cmpl == -1), n + 2);
int R = min(rn + i - (cmpr == 1), n + 2);
//fprintf(stderr, "i = %d, cmpl = %d, cmpr = %d, L = %d, R = %d
", i, cmpl, cmpr, L, R);
dp[i] = (L <= R) ? add(suf[L], mod - suf[R + 1]) : 0;
//fprintf(stderr, "dp[%d] = %d
", i, dp[i]);
suf[i] = add(suf[i + 1], dp[i]);
}
printf("%d
", dp[1]);
return 0;
}
F - The Shortest Statement
题意:
给你一个图,有(n)个点,(m)条边,保证(m-nle 20)。有(q)次询问,每次询问求两个点(x,y)之间的最短路。
题解:
我们把这个图的其中一个生成树给拿出来,那么还有最多(21)条边不在这个生成树上。
这个最短路可能有两种:经过不在生成树上的边,和不经过的。
不经过生成树上的边的路径就只有(1)条,直接在生成树上找lca,然后(d=根到x的距离+根到y的距离-2 imes 根到lca的距离)
然后考虑经过不在生成树上的边。
如果一个路径经过一个边,那么它必定经过这条边的两个端点。所以我们对所有这些不在生成树上的边的端点跑一遍dijkstra,这些点最多只有(42)个。设(dist_{i,j})表示编号为(i)的这种点到原图上(j)号点的距离。那么经过不在生成树上的边的最短路(ans=minlimits_{i=0}^{41}{dist_{i,x}+dist_{i,y}})
最后把这两种最短路取(min)即可。
Code:
// Code by H~$~C
#include <bits/stdc++.h>
using namespace std;
#define epoch_time chrono::steady_clock::now().time_since_epoch().count()
static mt19937 __gen(epoch_time);
#define random_shuffle(begin, end) shuffle(begin, end, __gen)
#define fi first
#define se second
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
constexpr int inf = 0x3f3f3f3f;
constexpr ll lnf = 0x3f3f3f3f3f3f3f3f;
constexpr int Maxn = 100005;
#define int ll
vector<pair<int, int>> g[Maxn];
int n, m, q;
int par[Maxn][20], dep[Maxn];
ll dist[Maxn][50], deep[Maxn];
vector<int> vec;
bool vis[Maxn], low[Maxn];
void dfs(int u, int fa) {
vis[u] = true;
for (auto pr: g[u]) {
int v = pr.fi;
if (v != fa) {
if (!vis[v]) {
par[v][0] = u;
dep[v] = dep[u] + 1;
deep[v] = deep[u] + pr.se;
dfs(v, u);
}
else low[u] = low[v] = true;
}
}
}
inline int get_lca(int u, int v) {
if (dep[u] > dep[v]) swap(u, v);
for (int i = 0; i < 20; ++i)
if ((dep[v] - dep[u]) >> i & 1)
v = par[v][i];
if (u == v) return v;
for (int i = 19; i >= 0; --i)
if (par[u][i] != par[v][i])
u = par[u][i], v = par[v][i];
return par[u][0];
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
int u, v; ll w;
cin >> u >> v >> w;
g[u].push_back({v, w});
g[v].push_back({u, w});
}
par[1][0] = 0;
dfs(1, 1);
for (int j = 1; j < 20; ++j)
for (int i = 1; i <= n; ++i)
par[i][j] = par[par[i][j - 1]][j - 1];
for (int i = 1; i <= n; ++i)
if (low[i]) vec.push_back(i);
for (int num = 0; num < (int)vec.size(); ++num) {
for (int i = 1; i <= n; ++i) dist[i][num] = lnf;
priority_queue<pair<ll, int>> pq;
dist[vec[num]][num] = 0;
pq.push({0, vec[num]});
while (!pq.empty()) {
ll d = -pq.top().fi;
int u = pq.top().se;
pq.pop();
if (d != dist[u][num]) continue;
for (auto pr: g[u]) {
int v = pr.fi;
ll w = pr.se;
if (dist[v][num] > dist[u][num] + w) {
dist[v][num] = dist[u][num] + w;
pq.push({-dist[v][num], v});
}
}
}
}
cin >> q;
while (q--) {
int u, v;
cin >> u >> v;
int lca = get_lca(u, v);
ll ans = deep[u] + deep[v] - deep[lca] * 2;
for (int i = 0; i < (int)vec.size(); ++i)
ans = min(ans, dist[u][i] + dist[v][i]);
cout << ans << endl;
}
return 0;
}
G - Distinctification
题意:
若多重集合(S={(a_i,b_i)|1le ile k}),定义一个操作为:
- 若存在(j),使得(a_i=a_j),则可以花(b_i)的代价使(a_i=a_i+1)
- 若存在(j),使得(a_i=a_j+1),则可以花(-b_i)的代价使(a_i=a_i-1)
你可以进行任何次数的操作。
定义(f(S))为最小的代价,使得你可以花(f(S))的代价使(S)里面的所有(a_i)不同。(f(S))可以为负数。
你现在有(n)次询问,每次询问是在当前的(S)里面插入((a_i,b_i))后询问(f(S))。开始时(S=varnothing)。保证(1le a_ile 2 imes 10^5,1le b_ile n),且所有的(b_i)不同。
题解:
很容易地可以观察到当且仅当(|a_i-a_j|le1),则可以交换(a_i,a_j)
首先先考虑若(S)里的(a_i)是一段连续值域,且互不相同怎么求。我们可以想到,(b_i)越大,就要尽可能的减少(a_i)。又因为(S)里的(a_i)是一段连续的值域且互不相同,所以(S)里的(a_i)可以随便排,所以最后的(S)应该是:对于任意(a_i<a_j),(b_i>b_j)。此时的代价应为((a'_1-a_1) imes b_1+(a'_2-a_2) imes b_2+dots+(a'_{|S|}-a_{|S|}) imes b_{|S|}),即((a'_1 imes b_1+a'_2 imes b_2+dots+a'_{|S|} imes b_{|S|})-(a_1 imes b_1+a_2 imes b_2+dots+a_{|S|} imes b_{|S|}))。
又因为当且仅当(|a_i-a_j|le1),则可以交换(a_i,a_j),所以(S)的每一个极长(a_i)连续值域段是互相独立的,所以对于任意一个(S),(f(S)=(a'_1 imes b_1+a'_2 imes b_2+dots+a'_{|S|} imes b_{|S|})-(a_1 imes b_1+a_2 imes b_2+dots+a_{|S|} imes b_{|S|}))。
显然(a_1 imes b_1+a_2 imes b_2+dots+a_{|S|} imes b_{|S|})是很好求的,所以问题就转化为了求(a'_1 imes b_1+a'_2 imes b_2+dots+a'_{|S|} imes b_{|S|})。记这个式子为after。
我们先考虑在一个极长(a_i)连续值域段里面插入((a_i,b_i))怎么做。我们维护这个极长(a_i)连续值域的最左端(beg),这样将(b)从小到大排序后可得(after=b_1 imes (beg+k-1)+b_2 imes (beg+k-2)+dots+b_k imes beg),即(after=beg imes(b_1+b_2+dots+b_k)+(b_1 imes(k-1)+b_2 imes(k-2)+dots+b_k imes0))。现在问题就是动态地在一个排好序的数组(b)中插入(x),插入完后求上面那个式子。(beg imes(b_1+b_2+dots+b_k))特别好求,每一次插入(x),答案就(+x imes beg)。对于(b_1 imes(k-1)+b_2 imes(k-2)+dots+b_k imes0)我们可以用treap维护数组(b),每一次插入(x)时把(b)分成(b_{1..pos})和(b_{pos+1..k})。那么这个时候(b_{1..pos})里的每一个(b)对答案的贡献(+1),(x)对答案的贡献(+(k-pos) imes x)。这个可以在treap上维护size和sum来快速实现。于是动态插入((a_i,b_i))的单次时间复杂度是(O(log k))的。
接着我们考虑如何合并两个极长(a_i)连续值域段(S,T)。不妨设(|S|le|T|)。一个很直接的想法就是直接将(S)里面的(b_i)依次插入到(T)里面。这样的时间复杂度是(O(|S|log|T|))。
然后我们考虑在一个普通的集合(S)里面插入((a_i,b_i))。
我们用并查集维护那些极长(a_i)连续值域段已经合并。
如果((a_i,b_i))属于之前的一个极长(a_i)连续值域段,那么直接在那个极长(a_i)连续值域段里插入((a_i,b_i)),同时还要占据这个连续值域段的右端点的右边一位。否则就新开创一个极长(a_i)连续值域段,长度为(1)。
在这之后还要看((a_i,b_i))所属的极长(a_i)连续值域段是否能和它旁边的两个极长(a_i)连续值域段合并。两个极长(a_i)连续值域段(S,T)能合并的条件是(r(S)+1=l(T))或(r(T)+1=l(S)),其中(r(S))代表(S)的右端点,(l(S))代表(S)的左端点。
每一次合并的时间复杂度是(O(min(|S|,|T|)log N))的。由于是把小集合的元素的往大集合里面插入进去,每一个元素最多只会被插入(log N)次,所以总时间复杂度最坏情况下是(O((N imes log N) imes log N)=O(Nlog^2N))。
Code:
// Code by H~$~C
#include <bits/stdc++.h>
using namespace std;
#define epoch_time chrono::steady_clock::now().time_since_epoch().count()
static mt19937 __gen(epoch_time);
#define random_shuffle(begin, end) shuffle(begin, end, __gen)
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
using pii = pair<int, int>;
constexpr int inf = 0x3f3f3f3f;
constexpr ll lnf = 0x3f3f3f3f3f3f3f3f;
inline int randint() {
return uniform_int_distribution<int>()(__gen);
}
constexpr int Maxn = 400005;
int n;
ll before, after;
struct node {
int size, fix;
node *l, *r;
int x;
ll sum;
node() { }
node(int x, node *l = NULL, node *r = NULL)
: x(x), l(l), r(r), size(1), fix(randint()) {
sum = x;
}
inline int lsize() const { return l ? l->size : 0; }
inline int rsize() const { return r ? r->size : 0; }
inline ll lsum() const { return l ? l->sum : 0; }
inline ll rsum() const { return r ? r->sum : 0; }
inline void pushup() {
size = lsize() + rsize() + 1;
sum = lsum() + rsum() + x;
}
} pool[Maxn * 20], *cur_ptr = pool;
inline node *newnode(int x, node *l = NULL, node *r = NULL) {
return &(*cur_ptr++ = node(x, l, r));
}
node *merge(node *l, node *r) {
if (!l || !r) return l ? l : r;
if (l->fix < r->fix) {
l->r = merge(l->r, r);
l->pushup();
return l;
}
else {
r->l = merge(l, r->l);
r->pushup();
return r;
}
}
void split(node *p, int k, node *&l, node *&r) {
if (!p) l = r = NULL;
else {
if (p->x <= k) {
l = p;
split(p->r, k, l->r, r);
l->pushup();
}
else {
r = p;
split(p->l, k, l, r->l);
r->pushup();
}
}
}
struct adjacent {
node *root;
int beg;
ll all_sum, inner_sum;
adjacent() = default;
inline void update_all() {
all_sum = inner_sum;
if (root) all_sum += root->sum * beg;
}
void insert(int b) {
node *A, *B;
split(root, b, A, B);
if (A) inner_sum += A->sum;
if (B) inner_sum += 1LL * B->size * b;
root = merge(merge(A, newnode(b)), B);
update_all();
}
void insert_treap(node *p) {
if (!p) return ;
insert(p->x);
insert_treap(p->l);
insert_treap(p->r);
}
friend adjacent operator + (adjacent x, adjacent y) {
int xsize = x.root ? x.root->size : 0;
int ysize = y.root ? y.root->size : 0;
if (xsize < ysize) swap(x, y);
x.beg = min(x.beg, y.beg);
x.insert_treap(y.root);
x.update_all();
return x;
}
};
adjacent adj[Maxn];
int fa[Maxn], sz[Maxn];
inline int fnd(int x) {
return fa[x] == x ? x : fa[x] = fnd(fa[x]);
}
void unite(int x, int y) {
x = fnd(x), y = fnd(y);
if (x == y) return;
if (sz[x] < sz[y]) swap(x, y);
fa[y] = x, sz[x] += sz[y];
after -= adj[x].all_sum;
after -= adj[y].all_sum;
adj[x] = adj[x] + adj[y];
after += adj[x].all_sum;
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
cin >> n;
before = 0;
set<int> rest;
for (int i = 0; i < Maxn; ++i) {
rest.insert(i);
fa[i] = i, sz[i] = 1;
}
for (int i = 1; i <= n; ++i) {
int a, b;
cin >> a >> b;
before += 1LL * a * b;
int pos = *rest.lower_bound(a);
rest.erase(pos);
adj[pos].beg = pos;
adj[pos].insert(b);
after += adj[pos].all_sum;
if (rest.find(pos - 1) == rest.end()) unite(pos - 1, pos);
if (rest.find(pos + 1) == rest.end()) unite(pos + 1, pos);
cout << after - before << endl;
}
return 0;
}
以上是关于CF Educational Round 51 题解的主要内容,如果未能解决你的问题,请参考以下文章
CF Educational Codeforces Round 57划水记
[Educational Codeforces Round#22]
补题Educational Codeforces Round 85 (Rated for Div. 2)
Educational Codeforces Round 55 题解