zoukankan      html  css  js  c++  java
  • AtCoder Grand Contest 045 部分题目简要题解

    从这里开始

      因为巨大多无可奉告的原因,所以咕得非常厉害。有空再补 EF 好了

      F 补上了,E 在路上了。神仙 jerome_wei 给我说 F 比 B 简单,被打爆哩。

    Problem A Xor Battle

      考虑如果 1 能选的某个数 $a$ 后面 0 能选的数都能表示出它,显然是 0 必胜。

      否则考虑考虑到这一位的异或和为 $s$,如果 0 能获胜,那么说明 $s XOR a$ 以及 $s$ 都能被后面的数线性表示出来,这推出 $a$ 能被线性表示出来,这显然和条件矛盾。因此此时 1 必胜。

      感觉做复杂了,去看瞄了一眼题解,直接 dp 用线性基维护一下 dp 值为真的位置就行了。不会 dp 石锤了。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    
    #define ll long long
    
    const int bzmax = 60;
    
    typedef class LinearBasis {
      public:
        ll a[bzmax];
      
        void insert(ll x) {
          for (int i = bzmax; i-- && x; ) {
            if ((x >> i) & 1) {
              x ^= a[i];
            }
            if ((x >> i) & 1) {
              a[i] = x;
              break;
            }
          }
        }
        bool query(ll x) {
          for (int i = bzmax; i-- && x; ) {
            if ((x >> i) & 1) {
              x ^= a[i];
            }
          }
          return !x;
        }
        
        void reset() {
          memset(a, 0, sizeof(a));
        }
    } LinearBasis;
    
    const int N = 205;
    
    int T, n;
    ll a[N];
    char s[N];
    LinearBasis lb;
    
    void solve() {
      lb.reset();
      scanf("%d", &n);
      for (int i = 1; i <= n; i++) {
        scanf("%lld", a + i);
      }
      scanf("%s", s + 1);
      for (int i = n; i; i--) {
        if (s[i] == '1') {
          if (!lb.query(a[i])) {
            puts("1");
            return;
          }  
        } else {
          lb.insert(a[i]);
        }
      }
      puts("0");
    }
    
    int main() {
      scanf("%d", &T);
      while (T--) {
        solve();
      }
      return 0;
    }

    Problem B 01 Unbalanced

      当没有通配符的时候,把 0 替换为 -1,考虑前缀和,显然答案是最大前缀和和最小前缀和的差。

      考虑先把通配符全部填成 1。从右到左依次考虑每个通配符填成的 1,如果把它换成 -1 能够使得最大的和减少 2,那么就换。假如不操作这一步,那么把之后操作的一步放到这里来显然不会更劣。

      这样有点小问题,因为可以使得最大的和减少 1。首先可以发现这样的次数至多 1 次,因为操作了两次就把前一次删掉,显然不会更劣。容易发现操作的这一次位置应该尽量靠右。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 1e6 + 5;
    
    int n;
    char s[N];
    int sum[N], pmx[N], pmi[N];
    
    int main() {
      scanf("%s", s + 1);
      n = strlen(s + 1);
      sum[0] = 0;
      pmx[0] = 0;
      pmi[0] = 0;
      for (int i = 1; i <= n; i++) {
        sum[i] = sum[i - 1] + 1 - 2 * (s[i] == '0');
        pmx[i] = max(pmx[i - 1], sum[i]);
        pmi[i] = min(pmi[i - 1], sum[i]);
      }
      int suf_mi = sum[n], suf_mx = sum[n];
      int ans = pmx[n] - pmi[n];
      for (int i = n; i; i--) {
        suf_mi = min(suf_mi, sum[i]);
        suf_mx = max(suf_mx, sum[i]);
        if (s[i] == '?') {
          if (pmx[i - 1] + 1 < suf_mx) {
            suf_mx -= 2;
            suf_mi -= 2;
            ans = min(ans, max(pmx[i - 1], suf_mx) - min(pmi[i - 1], suf_mi));
          }
        }
      }
      suf_mi = sum[n], suf_mx = sum[n];
      bool found = false;
      for (int i = n; i; i--) {
        suf_mi = min(suf_mi, sum[i]);
        suf_mx = max(suf_mx, sum[i]);
        if (s[i] == '?') {
          if (pmx[i - 1] + 1 == suf_mx) {
            ans = min(ans, max(pmx[i - 1], suf_mx - 2) - min(pmi[i - 1], suf_mi - 2));
            if (!found) {
              suf_mi -= 2;
              suf_mx -= 2;
              found = true;
            }
          } else if (pmx[i - 1] + 1 < suf_mx) {
            suf_mx -= 2;
            suf_mi -= 2;
            ans = min(ans, max(pmx[i - 1], suf_mx) - min(pmi[i - 1], suf_mi));
          }
        }
      }
      printf("%d
    ", ans);
      return 0;
    }

    Problem C Range Set

      不妨设 $A > B$,显然对于每种局面存在一种方案使得至少使用一次 $A$ 操作。

      如果能够进行一次 $A$ 操作,从左到右填出最后一次左边的颜色,再从右到左填出右边的颜色即可。

      然后考虑计算一次 $A$ 操作都进行不了的方案数,这个等价于计算任意两个相邻的 1 的长度小于 $B$ 的连续段之间的长度小于 $A$。

      这个简单 dp 一下就好了。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    
    #define ll long long
    
    void exgcd(int a, int b, int& x, int& y) {
      if (!b) {
        x = 1, y = 0;
      } else {
        exgcd(b, a % b, y, x);
        y -= (a / b) * x;
      }
    }
    
    int inv(int a, int n) {
      int x, y;
      exgcd(a, n, x, y);
      return (x < 0) ? (x + n) : (x);
    }
    
    const int Mod = 1e9 + 7;
    
    template <const int Mod = :: Mod>
    class Z {
      public:
        int v;
    
        Z() : v(0) {	}
        Z(int x) : v(x){	}
        Z(ll x) : v(x % Mod) {	}
    
        friend Z operator + (const Z& a, const Z& b) {
          int x;
          return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
        }
        friend Z operator - (const Z& a, const Z& b) {
          int x;
          return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
        }
        friend Z operator * (const Z& a, const Z& b) {
          return Z(a.v * 1ll * b.v);
        }
        friend Z operator ~(const Z& a) {
          return inv(a.v, Mod);
        }
        friend Z operator - (const Z& a) {
          return Z(0) - a;
        }
        Z& operator += (Z b) {
          return *this = *this + b;
        }
        Z& operator -= (Z b) {
          return *this = *this - b;
        }
        Z& operator *= (Z b) {
          return *this = *this * b;
        }
    };
    
    Z<> qpow(Z<> a, int p) {
      Z<> rt = Z<>(1), pa = a;
      for ( ; p; p >>= 1, pa = pa * pa) {
        if (p & 1) {
          rt = rt * pa;
        }
      }
      return rt;
    }
    
    typedef Z<> Zi;
    
    const int N = 5005;
    
    int n, A, B;
    Zi f[N], g[N][2];
    
    int main() {
      scanf("%d%d%d", &n, &A, &B);
      if (A > B) {
        swap(A, B);
      }
      f[0] = 1;
      for (int i = 1; i <= B; i++) {
        f[i] = f[i - 1];
        for (int j = A; j <= i; j++) {
          if (j == i) {
            f[i] += 1;
          } else {
            f[i] += f[i - j - 1];
          }
        }
      }
      g[0][0] = 1;
      for (int i = 1; i < B; i++) {
        g[i][0] = f[i - 1]; 
      }
      for (int i = 1; i <= n; i++) {
        for (int j = 1; j < B && j <= i; j++) {
          g[i][0] += g[i - j][1] * ((j == 1) ? 1 : f[j - 2]);
        }
        for (int j = 1; j < A && j <= i; j++) {
          g[i][1] += g[i - j][0];
        }
      }
      Zi ans = g[n][1];
      for (int i = 1; i < B; i++) {
        ans += g[n - i][1] * f[i - 1];
      }
      ans = qpow(2, n) - ans;
      printf("%d
    ", ans.v);
      return 0;
    }

    Problem D Lamps and Buttons

      排序排列对应的若干个环。显然每个灯属于的环必选包含前 $A$ 个中的某一个。

      考虑最优策略显然是:当还未全点亮时,选择一个点亮的灯,操作它, 如果它熄灭了就失败,否则显然可以点亮它所在的环。

      所以问题等价于计算满足满足下列条件的环排列划分的方案数:

    • 每个环都包含至少一个点亮的灯
    • 第一个自环后面的环只包含前 $A$ 个灯中的灯。

      考虑枚举第一个自环的位置,然后对前面每个环的大小都大于 1 进行容斥。

      首先计算出 $f_{i, j}$ 表示考虑前 $i$ 个灯,有 $j$ 个被硬点是自环的方案数。

      然后考虑怎么计算答案。注意到计算答案之和 $j$ 有关,设 $t_i$ 表示当有 $j$ 个灯被硬点为自环时,将剩下的灯加入环排列的方案数。

      那么考虑 $f_{i - 1, j}$ 贡献到答案的系数。枚举后面 $A - i$ 个灯有多少个插入前 $i$ 个的环排列中,那么有:

    $$
    sum_{k=0}^{A-i}(i - j - 1)^{overline{k}} inom{A-i}{k} (A - i - k)! t_{j + 1 + m - i}
    $$

      将组合数拆成阶乘表示的形式,然后提出 $(A - i)!$,然后设 $d = i - j,  l = m - i$,很容易在 $O(n^2)$ 的时间内预处理系数。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    
    #define ll long long
    
    void exgcd(int a, int b, int& x, int& y) {
      if (!b) {
        x = 1, y = 0;
      } else {
        exgcd(b, a % b, y, x);
        y -= (a / b) * x;
      }
    }
    
    int inv(int a, int n) {
      int x, y;
      exgcd(a, n, x, y);
      return (x < 0) ? (x + n) : (x);
    }
    
    const int Mod = 1e9 + 7;
    
    template <const int Mod = :: Mod>
    class Z {
      public:
        int v;
    
        Z() : v(0) {	}
        Z(int x) : v(x){	}
        Z(ll x) : v(x % Mod) {	}
    
        friend Z operator + (const Z& a, const Z& b) {
          int x;
          return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
        }
        friend Z operator - (const Z& a, const Z& b) {
          int x;
          return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
        }
        friend Z operator * (const Z& a, const Z& b) {
          return Z(a.v * 1ll * b.v);
        }
        friend Z operator ~(const Z& a) {
          return inv(a.v, Mod);
        }
        friend Z operator - (const Z& a) {
          return Z(0) - a;
        }
        Z& operator += (Z b) {
          return *this = *this + b;
        }
        Z& operator -= (Z b) {
          return *this = *this - b;
        }
        Z& operator *= (Z b) {
          return *this = *this * b;
        }
    };
    
    Z<> qpow(Z<> a, int p) {
      Z<> rt = Z<>(1), pa = a;
      for ( ; p; p >>= 1, pa = pa * pa) {
        if (p & 1) {
          rt = rt * pa;
        }
      }
      return rt;
    }
    
    typedef Z<> Zi;
    
    const int N = 1e7 + 5, M = 5005;
    
    vector<Zi> fac, _fac;
    Zi comb[M][M];
    
    void init_fac(int n) {
      fac.resize(n + 1);
      _fac.resize(n + 1);
      fac[0] = 1;
      for (int i = 1; i <= n; i++) {
        fac[i] = fac[i - 1] * i;
      }
      _fac[n] = ~fac[n];
      for (int i = n; i; i--) {
        _fac[i - 1] = _fac[i] * i;
      }
    }
    void init(int n) {
      comb[0][0] = 1;
      for (int i = 1; i <= n; i++) {
        comb[i][0] = 1;
        for (int j = 1; j <= i; j++) {
          comb[i][j] = comb[i - 1][j - 1] + comb[i - 1][j];
        }
      }
    }
    
    int n, m;
    Zi f[M][M], g[M][M], t[M];
    
    int main() {
      scanf("%d%d", &n, &m);
      init_fac(n);
      init(m);
    
      for (int i = 0; i < m; i++) {
        t[i] = fac[n - 1 - i] * _fac[m - 1 - i];
      }
      for (int d = 1; d <= m; d++) {
        g[d][0] = t[m - d + 1];
        if (d == 1) {
          for (int j = 1; d + j <= m; j++) {
            g[d][j] = g[d][j - 1];
          }
        } else {
          for (int j = 1; d + j <= m; j++) {
            g[d][j] = g[d][j - 1] + fac[d + j - 2] * _fac[d - 2] * _fac[j] * t[m - d + 1 - j];
          }
        }
        for (int j = 1; d + j <= m; j++) {
          g[d][j] *= fac[j];
        }
      }
      f[0][0] = 1;
      Zi ans = 0;
      for (int i = 1; i <= m; i++) {
        f[i][0] = f[i - 1][0] * i;
        for (int j = 1; j <= i; j++) {
          f[i][j] = f[i - 1][j] * (i - j) - f[i - 1][j - 1];
        }
        for (int j = 0; j < i; j++) {
          ans += f[i - 1][j] * g[i - j][m - i];
        }
      }
      for (int i = 0; i < m; i++) {
        ans += f[m][i] * t[i];
      }
      printf("%d
    ", ans.v);
      return 0;
    }

    Problem E Fragile Balls

      挖坑待填,明天再说.jpeg

    Problem F Division into Multiples

      不难做一些处理让 $A, B, C$ 两两互质。

      考虑合法的点集 $S = {(x, y) : C | ax + by, x + y > 0, x geqslant 0, y geqslant 0}$ 。显然只用考虑 $S$ 中满足左下角不存在其他点的点。

      先考虑对于 $x = i$,$y$ 的最小可能的解 $f_i$。显然这个是 $yB equiv -iA pmod{C}$ 的解。容易发现 $i$ 和 $i + 1$ 最小的解之差在模 $C$ 意义下是一个定值,即 $-i AB^{-1}$ 在模 $C$ 意义下的值 $d$。

      考虑有一个点,初始在 $(0, 0)$,每次横纵坐标分别 +1,然后纵坐标对 $d$ 取模,横坐标对 $C$ 取模,显然 $f_i$ 前缀最小值与这里时刻 $i$ 停留在 $x$ 轴上纵坐标的前缀最大值一一对应。这个过程可以用类欧几里得来模拟。

      显然,前缀最小值可以在一个有 $O(log V)$ 段的下凸壳上。

      考虑怎么求答案。

      先二分答案 $mid$,然后考虑把 $mid$ 个函数 $g$ 做 $min$ 卷积。$g_i$ 表示 $x$ 为 $i$ 时,取到的最小的 $y$。

      直接取前缀 min 没法做卷积。但是注意到前缀 min 全都在凸壳上,并且凸壳上相邻两点 $(x_i, y_i), (x_{i + 1}, y_{i + 1})$ 的横坐标之差 $x_{i + 1} - x_i$ 单调不下降,纵坐标之差 $y_{i + 1} - y_i$ 单调不上升。假设将 $g$ 的凸壳自己和自己做一次闵可夫斯基和得到点集有一个点不在凸壳上,那么有:

       显然这样话会破坏这个性质。

      因此直接对 $g$ 的凸壳做 $mid$ 次闵可夫斯基和再取前缀 min 就可以了。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    
    #define ll long long
    
    ll gcd(ll a, ll b) {
      return b ? gcd(b, a % b) : a;
    }
    void exgcd(ll a, ll b, ll& x, ll& y) {
      if (!b) {
        x = 1, y = 0;
      } else {
        exgcd(b, a % b, y, x);
        y -= (a / b) * x;
      }
    }
    ll inv(ll a, ll Mod) {
      ll x, y;
      exgcd(a, Mod, x, y);
      return x < 0 ? x + Mod : x;
    } 
    
    typedef class vec {
      public:
        ll dx, dy;
        int c;
    
        vec(ll dx, ll dy, int c) : dx(dx), dy(dy), c(c) { }
    } vec;
    
    int T;
    ll A, X, B, Y, C;
    vector<vec> conv;
    
    void solve() {
      scanf("%lld%lld%lld%lld%lld", &A, &X, &B, &Y, &C);
      ll g = gcd(A, B), ga, gb;
      A /= g, B /= g, C /= gcd(g, C);
      ga = gcd(A, C);
      A /= ga, Y /= ga, C /= ga;
      gb = gcd(B, C);
      B /= gb, X /= gb, C /= gb;
      if (C == 1) {
        printf("%lld
    ", X + Y);
        return;
      }
    
      conv.clear();
      ll W = C, H = A * inv(B, C) % C, x0 = 0, ia = inv(A, C), p = 0, np;
      while (W && H) {
        if (W >= H) {
          ll t = W / H;
          W -= t * H;
          x0 += t * H;
          np = x0 * B % C * ia % C;
          np += !np * C;
          conv.emplace_back((np - p) / t, H, t);
          p = np;
        } else {
          if (!(H % W)) { 
            conv.emplace_back(C - p, W, 1);
            break;
          } else {
            H %= W;
          }
        }
      }
    
      ll l = Y / C + 1, r = X + Y, mid;
      while (l <= r) {
        mid = (l + r) >> 1;
        ll ca = 0, cb = mid * C;
        for (auto v : conv) {
          ll ub = min((cb - Y - 1) / v.dy + 1, 1ll * v.c * mid);
          ub = min(ub, 1000000001ll);
          ca += v.dx * ub;
          cb -= v.dy * ub;
          if (ca > X || cb <= Y) {
            break;
          }
        }
        if (ca <= X && cb <= Y) {
          l = mid + 1;
        } else {
          r = mid - 1;
        }
      }
      printf("%d
    ", l - 1);
    }
    
    int main() {
      scanf("%d", &T);
      while (T--) {
        solve();
      }
      return 0;
    }

  • 相关阅读:
    display:flex 布局之 骰子
    vue 生命周期
    vue webpack 懒加载
    后台管理页面基本布局
    模拟ie9的placeholder
    常用的功能封装 pool.js
    六位数字字母验证码
    CommonJs AMD CMD
    项目封版后的总结
    jq 回到顶部
  • 原文地址:https://www.cnblogs.com/yyf0309/p/agc045.html
Copyright © 2011-2022 走看看