zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    django ajax使用
    vim--分屏快捷键
    django csrf
    django mysql使用
    官方文档地址
    图解http 学习
    Terms
    Data Center Group
    Misc
    FTDI CDM Drivers 2.06.00
  • 原文地址:https://www.cnblogs.com/libra9z/p/12445778.html
Copyright © 2011-2022 走看看