码迷,mamicode.com
首页 > 其他好文 > 详细

CF Educational Round 51 题解

时间:2020-03-08 23:32:50      阅读:106      评论:0      收藏:0      [点我收藏+]

标签:time   应该   否则   要求   git   pool   distinct   生成树   queue   

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-l\mod 2=1,l\le r\),要你把范围里面的所有的整数分成\(\dfrac{r-l+1}{2}\)个部分,每一个部分有两个数,且这两个数互质。如果能办到,就输出YES,然后输出方案;否则输出NO\(1\le l<r\le 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\n", i, i + 1);
  return 0;
}

C - Vasya and Multisets

题意:

给你一个多重集合\(S\),如果\(x\in S\)\(x\)\(S\)中只出现\(1\)次,则称\(x\)为好数。现在要你把\(S\)分成两个多重集合\(A,B\),使得\(A,B\)里面的好数的个数相等。如果办不到,输出NO\(1\le|S|\le 100\)

题解:

统计\(S\)中的好数的个数\(c\),如果\(c\mod2=0\),则直接把\(\dfrac{c}{2}\)个好数分给\(A\),其他的所有数都分给\(B\)。如果\(c\mod 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\times n\)的棋盘,可以对上面的格子黑白染色,求染色后棋盘上的颜色相同联通块的个数正好为\(k\)的染色方案数。\(1\le n\le 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]\)之间。\(0\le l\le r\le 10^{10^{6}},0\le S\le10^{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\neq 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\rightarrow|l|-1\),若\(S_{i+j}\neq 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\n", i, zl[i]);
  //for (int i = 1; i <= n + rn + 1; ++i) fprintf(stderr, "zr[%d] = %d\n", 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\n", i, cmpl, cmpr, L, R);
    dp[i] = (L <= R) ? add(suf[L], mod - suf[R + 1]) : 0;
    //fprintf(stderr, "dp[%d] = %d\n", i, dp[i]);
    suf[i] = add(suf[i + 1], dp[i]);
  }
  printf("%d\n", dp[1]);
  return 0;
}

F - The Shortest Statement

题意:

给你一个图,有\(n\)个点,\(m\)条边,保证\(m-n\le 20\)。有\(q\)次询问,每次询问求两个点\(x,y\)之间的最短路。

题解:

我们把这个图的其中一个生成树给拿出来,那么还有最多\(21\)条边不在这个生成树上。

这个最短路可能有两种:经过不在生成树上的边,和不经过的。

不经过生成树上的边的路径就只有\(1\)条,直接在生成树上找lca,然后\(d=根到x的距离+根到y的距离-2\times 根到lca的距离\)

然后考虑经过不在生成树上的边。

如果一个路径经过一个边,那么它必定经过这条边的两个端点。所以我们对所有这些不在生成树上的边的端点跑一遍dijkstra,这些点最多只有\(42\)个。设\(dist_{i,j}\)表示编号为\(i\)的这种点到原图上\(j\)号点的距离。那么经过不在生成树上的边的最短路\(ans=\min\limits_{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)|1\le i\le 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\)。保证\(1\le a_i\le 2\times 10^5,1\le b_i\le 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)\times b_1+(a'_2-a_2)\times b_2+\dots+(a'_{|S|}-a_{|S|})\times b_{|S|}\),即\((a'_1\times b_1+a'_2\times b_2+\dots+a'_{|S|}\times b_{|S|})-(a_1\times b_1+a_2\times b_2+\dots+a_{|S|}\times b_{|S|})\)

又因为当且仅当\(|a_i-a_j|\le1\),则可以交换\(a_i,a_j\),所以\(S\)的每一个极长\(a_i\)连续值域段是互相独立的,所以对于任意一个\(S\)\(f(S)=(a'_1\times b_1+a'_2\times b_2+\dots+a'_{|S|}\times b_{|S|})-(a_1\times b_1+a_2\times b_2+\dots+a_{|S|}\times b_{|S|})\)

显然\(a_1\times b_1+a_2\times b_2+\dots+a_{|S|}\times b_{|S|}\)是很好求的,所以问题就转化为了求\(a'_1\times b_1+a'_2\times b_2+\dots+a'_{|S|}\times b_{|S|}\)。记这个式子为after。

我们先考虑在一个极长\(a_i\)连续值域段里面插入\((a_i,b_i)\)怎么做。我们维护这个极长\(a_i\)连续值域的最左端\(beg\),这样将\(b\)从小到大排序后可得\(after=b_1\times (beg+k-1)+b_2\times (beg+k-2)+\dots+b_k\times beg\),即\(after=beg\times(b_1+b_2+\dots+b_k)+(b_1\times(k-1)+b_2\times(k-2)+\dots+b_k\times0)\)。现在问题就是动态地在一个排好序的数组\(b\)中插入\(x\),插入完后求上面那个式子。\(beg\times(b_1+b_2+\dots+b_k)\)特别好求,每一次插入\(x\),答案就\(+x\times beg\)。对于\(b_1\times(k-1)+b_2\times(k-2)+\dots+b_k\times0\)我们可以用treap维护数组\(b\),每一次插入\(x\)时把\(b\)分成\(b_{1..pos}\)\(b_{pos+1..k}\)。那么这个时候\(b_{1..pos}\)里的每一个\(b\)对答案的贡献\(+1\)\(x\)对答案的贡献\(+(k-pos)\times 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\times \log N)\times \log N)=O(N\log^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 题解

标签:time   应该   否则   要求   git   pool   distinct   生成树   queue   

原文地址:https://www.cnblogs.com/libra9z/p/12445778.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!