CodeForces Gym 102028 简要题解

Xu Xiake in Henan Province

模拟。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int tt;
  scanf("%d", &tt);
  while (tt--) {
    int total = 0;
    for (int i = 0; i < 4; ++i) {
      int x;
      scanf("%d", &x);
      if (x) {
        ++total;
      }
    }
    switch (total) {
      case 0:
        puts("Typically Otaku");
        break;
      case 1:
        puts("Eye-opener");
        break;
      case 2:
        puts("Young Traveller");
        break;
      case 3:
        puts("Excellent Traveller");
        break;
      case 4:
        puts("Contemporary Xu Xiake");
        break;
    }
  }
  return 0;
}

Ultraman vs. Aodzilla and Bodzilla

贪心,记 f ( x ) f(x) f(x) 表示造成 x x x 点伤害需要的时间,那么有两种方案:

  • 共打 f ( H P A + H P B ) f(HP_A + HP_B) f(HPA+HPB) 轮,在第 f ( H P A ) f(HP_A) f(HPA) 轮时打败 A A A
  • 共打 f ( H P A + H P B ) f(HP_A + HP_B) f(HPA+HPB) 轮,在第 f ( H P B ) f(HP_B) f(HPB) 轮时打败 B B B

输出方案贪心即可。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int tt;
  scanf("%d", &tt);
  while (tt--) {
    int hp_a, hp_b, attack_a, attack_b;
    scanf("%d %d %d %d", &hp_a, &hp_b, &attack_a, &attack_b);
    auto get_sum = [&](int x) {
      return (long long)x * (x + 1) >> 1;
    };
    auto get_round = [&](int x) {
      int result = 1;
      while (get_sum(result) < x) {
        ++result;
      }
      return result;
    };
    int round_a = get_round(hp_a), round_b = get_round(hp_b), round_total = get_round(hp_a + hp_b);
    long long answer = min((long long)attack_a * round_a + (long long)attack_b * round_total, (long long)attack_a * round_total + (long long)attack_b * round_b);
    string result(round_total, 'B');
    if ((long long)attack_a * round_a + (long long)attack_b * round_total == answer) {
      string current(round_total, 'A');
      for (int i = round_a; i < round_total; ++i) {
        current[i] = 'B';
      }
      if (get_sum(round_a) - hp_a > get_sum(round_total) - hp_a - hp_b) {
        current[get_sum(round_a) - hp_a - 1] = 'B';
      }
      result = min(result, current);
    }
    if ((long long)attack_a * round_total + (long long)attack_b * round_b == answer) {
      string current(round_total, 'B');
      for (int i = round_b; i < round_total; ++i) {
        current[i] = 'A';
      }
      long long remain_b = get_sum(round_b) - hp_b, remain_a = remain_b - (get_sum(round_total) - hp_a - hp_b);
      int last = -1;
      for (int i = 0; i < round_a; ++i) {
        if (remain_b >= i + 1) {
          last = i;
          current[i] = 'A';
          remain_b -= i + 1;
          remain_a -= i + 1;
        }
      }
      if (remain_a > 0) {
        current[last] = 'B';
        current[last + remain_a] = 'A';
      }
      result = min(result, current);
    }
    cout << answer << " " << result << endl;
  }
  return 0;
}

Supreme Command

考虑坐标时,显然两维独立。考虑一个点的位置,可以表示成 max ⁡ ( l , min ⁡ ( x , r ) ) + d \max(l, \min(x,r)) + d max(l,min(x,r))+d 的形式,可以快速维护。

再考虑第二问,不难发现棋盘可以用两条横线两条竖线划分成 9 9 9 部分,因为初始每行每列只有一个车,所以只有 4 4 4 个角的车会占领重复位置。在操作时移动分界线即可。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(0);
  cin.tie(0);
  int tt;
  cin >> tt;
  while (tt--) {
    int n, m;
    cin >> n >> m;
    vector<int> x(n), y(n);
    vector<int> px(n), py(n);
    for (int i = 0; i < n; ++i) {
      cin >> x[i] >> y[i];
      --x[i];
      --y[i];
      px[x[i]] = i;
      py[y[i]] = i;
    }
    int lx = 0, rx = n - 1, dx = 0;
    int ly = 0, ry = n - 1, dy = 0;
    int llx = -1, rrx = n;
    int lly = -1, rry = n;
    int ul = 0, ur = 0, dl = 0, dr = 0;
    while (m--) {
      string type;
      cin >> type;
      if (type == "U") {
        int shift;
        cin >> shift;
        int low = max(lx + dx - shift, 0);
        int high = max(rx + dx - shift, 0);
        if (low == high) {
          lx = rx = 0;
        } else {
          lx = rx - high + low;
        }
        dx = high - rx;
      } else if (type == "D") {
        int shift;
        cin >> shift;
        int low = min(lx + dx + shift, n - 1);
        int high = min(rx + dx + shift, n - 1);
        if (low == high) {
          lx = rx = 0;
        } else {
          rx = lx + high - low;
        }
        dx = low - lx;
      } else if (type == "L") {
        int shift;
        cin >> shift;
        int low = max(ly + dy - shift, 0);
        int high = max(ry + dy - shift, 0);
        if (low == high) {
          ly = ry = 0;
        } else {
          ly = ry - high + low;
        }
        dy = high - ry;
      } else if (type == "R") {
        int shift;
        cin >> shift;
        int low = min(ly + dy + shift, n - 1);
        int high = min(ry + dy + shift, n - 1);
        if (low == high) {
          ly = ry = 0;
        } else {
          ry = ly + high - low;
        }
        dy = low - ly;
      } else if (type == "?") {
        int id;
        cin >> id;
        --id;
        printf("%d %d\n", max(lx, min(rx, x[id])) + dx + 1, max(ly, min(ry, y[id])) + dy + 1);
      } else if (type == "!") {
        auto get = [&](int x) {
          return (long long)x * (x - 1) >> 1;
        };
        long long answer = 0;
        if (lx == rx && ly == ry) {
          printf("%lld\n", get(n));
        } else if (lx == rx) {
          printf("%lld\n", get(ly + 1) + get(n - ry));
        } else if (ly == ry) {
          printf("%lld\n", get(lx + 1) + get(n - rx));
        } else {
          while (llx < lx) {
            ++llx;
            int id = px[llx];
            if (y[id] <= lly) {
              ++ul;
            }
            if (y[id] >= rry) {
              ++ur;
            }
          }
          while (rrx > rx) {
            --rrx;
            int id = px[rrx];
            if (y[id] <= lly) {
              ++dl;
            }
            if (y[id] >= rry) {
              ++dr;
            }
          }
          while (lly < ly) {
            ++lly;
            int id = py[lly];
            if (x[id] <= llx) {
              ++ul;
            }
            if (x[id] >= rrx) {
              ++dl;
            }
          }
          while (rry > ry) {
            --rry;
            int id = py[rry];
            if (x[id] <= llx) {
              ++ur;
            }
            if (x[id] >= rrx) {
              ++dr;
            }
          }
          printf("%lld\n", get(ul) + get(ur) + get(dl) + get(dr));
        }
      }
    }
  }
  return 0;
}

Keiichi Tsuchiya the Drift King

分是否取到最大值两种情况讨论一下。

#include 

using namespace std;

const double pi = acos(-1);

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int tt;
  scanf("%d", &tt);
  while (tt--) {
    int a, b, r, d;
    scanf("%d %d %d %d", &a, &b, &r, &d);
    double angle = d * pi / 180;
    if (angle >= atan((double)b / (a + r))) {
      printf("%.9lf\n", sqrt(b * b + (a + r) * (a + r)) - r);
    } else {
      printf("%.9lf\n", (r + a) * cos(angle) + b * sin(angle) - r);
    }
  }
  return 0;
}

Resistors in Parallel

考虑最后选择了 ∏ i = 1 k p i \prod_{i=1}^k p_i i=1kpi ,那么其贡献是 ∏ i = 1 k p i + 1 p i \prod_{i=1}^k \frac{p_i+1}{p_i} i=1kpipi+1 ,选前若干小的质数即可。

def gcd(x, y):
    if y:
        return gcd(y, x % y)
    return x

tt = int(raw_input())
p = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231]
while tt:
    n = int(raw_input())
    num = 1
    den = 1
    for i in range(0, 200):
        if den * p[i] > n:
            break
        num *= p[i] + 1
        den *= p[i]
    g = gcd(den, num)
    print str(den / g) + '/' + str(num / g)
    tt = tt - 1

Honeycomb

模拟。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(0);
  cin.tie(0);
  string foo;
  int tt;
  {
    getline(cin, foo);
    stringstream ss;
    ss << foo;
    ss >> tt;
  }
  while (tt--) {
    int n, m;
    {
      getline(cin, foo);
      stringstream ss;
      ss << foo;
      ss >> n >> m;
    }
    vector<string> board(n * 4 + 3);
    for (int i = 0; i < n * 4 + 3; ++i) {
      getline(cin, board[i]);
      board[i].resize(m * 6 + 3);
    }
    vector<vector<int>> adj(n * m);
    int source = -1, sink = -1;
    for (int i = 0; i < n; ++i) {
      for (int j = 0; j < m; ++j) {
        int shift = j & 1 ? 2 : 0;
        if (board[i * 4 + 2 + shift][j * 6 + 4] == 'S') {
          source = i * m + j;
        }
        if (board[i * 4 + 2 + shift][j * 6 + 4] == 'T') {
          sink = i * m + j;
        }
        if (board[i * 4 + shift][j * 6 + 4] == ' ') {
          int ni = i - 1, nj = j;
          adj[i * m + j].push_back(ni * m + nj);
          adj[ni * m + nj].push_back(i * m + j);
        }
        if (board[i * 4 + 1 + shift][j * 6 + 7] == ' ') {
          int ni = i - !(j & 1), nj = j + 1;
          adj[i * m + j].push_back(ni * m + nj);
          adj[ni * m + nj].push_back(i * m + j);
        }
        if (board[i * 4 + 3 + shift][j * 6 + 7] == ' ') {
          int ni = i + (j & 1), nj = j + 1;
          adj[i * m + j].push_back(ni * m + nj);
          adj[ni * m + nj].push_back(i * m + j);
        }
      }
    }
    vector<int> dist(n * m, -1);
    queue<int> q;
    dist[source] = 1;
    q.push(source);
    while (!q.empty()) {
      int x = q.front();
      q.pop();
      for (auto y : adj[x]) {
        if (!~dist[y]) {
          dist[y] = dist[x] + 1;
          q.push(y);
        }
      }
    }
    printf("%d\n", dist[sink]);
  }
  return 0;
}

Shortest Paths on Random Forests

f ( n ) f(n) f(n) 表示 n n n 个点的森林个数, g ( n ) g(n) g(n) 表示 n n n 个点的森林可达点对的个数, h ( n ) h(n) h(n) 表示 n n n 个点森林可达点对距离平方之和, t ( n ) t(n) t(n) 表示 n n n 个点的树个数, F ( x ) , G ( x ) , H ( x ) , T ( x ) F(x), G(x), H(x), T(x) F(x),G(x),H(x),T(x) 表示其对应的EGF。那么:

  • F ( x ) = e T ( x ) F(x) = e^{T(x)} F(x)=eT(x)
  • G ( x ) G(x) G(x) 可以枚举一个连通块,带上贡献去卷 F ( x ) F(x) F(x)
  • 计算 H ( x ) H(x) H(x) 时, x 2 = 2 ( x 2 ) + x x^2 = 2\binom{x}{2} + x x2=2(2x)+x ,相当于从路径上无序地选 1 1 1 或者 2 2 2 条边的方案数。断掉这些边,将树划分成几个连通块,那么对于每个连通块,其方案数是 n n n^n nn (乘上 n 2 n^2 n2 是因为一条进一条出),卷起来就行了。
#include 

using namespace std;

const int md = 998244353;

inline void add(int &x, int y) {
  x += y;
  if (x >= md) {
    x -= md;
  }
}

inline void sub(int &x, int y) {
  x -= y;
  if (x < 0) {
    x += md;
  }
}

inline int mul(int x, int y) {
  return (long long)x * y % md;
}

inline int power(int x, int y) {
  int result = 1;
  for (; y; y >>= 1, x = mul(x, x)) {
    if (y & 1) {
      result = mul(result, x);
    }
  }
  return result;
}

namespace ntt {
int base = 1, root = -1, max_base = -1;
vector<int> roots = {0, 1}, rev = {0, 1}, inv = {1, 1};

void init() {
  int temp = md - 1;
  max_base = 0;
  while (!(temp & 1)) {
    temp >>= 1;
    ++max_base;
  }
  root = 2;
  while (true) {
    if (power(root, 1 << max_base) == 1 && power(root, 1 << max_base - 1) != 1) {
      break;
    }
    ++root;
  }
}

void ensure_base(int nbase) {
  if (max_base == -1) {
    init();
  }
  if (nbase <= base) {
    return;
  }
  assert(nbase <= max_base);
  rev.resize(1 << nbase);
  for (int i = 0; i < (1 << nbase); ++i) {
    rev[i] = rev[i >> 1] >> 1 | ((i & 1) << nbase - 1);
  }
  roots.resize(1 << nbase);
  while (base < nbase) {
    int z = power(root, 1 << max_base - 1 - base);
    for (int i = 1 << base - 1; i < 1 << base; ++i) {
      roots[i << 1] = roots[i];
      roots[i << 1 | 1] = mul(roots[i], z);
    }
    ++base;
  }
}

void dft(vector<int> &a) {
  int n = a.size(), zeros = __builtin_ctz(n);
  ensure_base(zeros);
  int shift = base - zeros;
  for (int i = 0; i < n; ++i) {
    if (i < rev[i] >> shift) {
      swap(a[i], a[rev[i] >> shift]);
    }
  }
  for (int i = 1; i < n; i <<= 1) {
    for (int j = 0; j < n; j += i << 1) {
      for (int k = 0; k < i; ++k) {
        int x = a[j + k], y = mul(a[j + k + i], roots[i + k]);
        a[j + k] = (x + y) % md;
        a[j + k + i] = (x + md - y) % md;
      }
    }
  }
}

void psub(vector<int> &a, vector<int> b) {
  if (a.size() < b.size()) {
    a.resize(b.size());
  }
  for (int i = 0; i < b.size(); ++i) {
    sub(a[i], b[i]);
  }
}

vector<int> pmul(vector<int> a, vector<int> b, bool equal = false) {
  int need = a.size() + b.size() - 1, nbase = 0;
  while (1 << nbase < need) {
    ++nbase;
  }
  ensure_base(nbase);
  int size = 1 << nbase;
  a.resize(size);
  b.resize(size);
  dft(a);
  if (equal) {
    b = a;
  } else {
    dft(b);
  }
  int inv = power(size, md - 2);
  for (int i = 0; i < size; ++i) {
    a[i] = mul(mul(a[i], b[i]), inv);
  }
  reverse(a.begin() + 1, a.end());
  dft(a);
  a.resize(need);
  return a;
}

vector<int> psqr(vector<int> a) {
  return pmul(a, a, true);
}

vector<int> pinv(vector<int> a) {
  int n = a.size(), m = n + 1 >> 1;
  if (n == 1) {
    return vector<int> (1, power(a[0], md - 2));
  } else {
    vector<int> b = pinv(vector<int> (a.begin(), a.begin() + m));
    int need = n << 1, nbase = 0;
    while (1 << nbase < need) {
      ++nbase;
    }
    ensure_base(nbase);
    int size = 1 << nbase;
    a.resize(size);
    b.resize(size);
    dft(a);
    dft(b);
    int inv = power(size, md - 2);
    for (int i = 0; i < size; ++i) {
      a[i] = mul(mul(md + 2 - mul(a[i], b[i]), b[i]), inv);
    }
    reverse(a.begin() + 1, a.end());
    dft(a);
    a.resize(n);
    return a;
  }
}

vector<int> pder(vector<int> a) {
  vector<int> b(a.size() - 1);
  for (int i = 0; i < b.size(); ++i) {
    b[i] = mul(a[i + 1], i + 1);
  }
  return b;
}

vector<int> pint(vector<int> a) {
  vector<int> b(a.size() + 1);
  while (inv.size() < b.size()) {
    int n = inv.size();
    inv.push_back(mul(md - md / n, inv[md % n]));
  }
  for (int i = 1; i < b.size(); ++i) {
    b[i] = mul(a[i - 1], inv[i]);
  }
  return b;
}

vector<int> pln(vector<int> a) {
  vector<int> b = pint(pmul(pder(a), pinv(a)));
  b.resize(a.size());
  return b;
}

vector<int> pexp(vector<int> a) {
  int n = a.size();
  if (n == 1) {
    return vector<int> (1, 1);
  } else {
    int m = n + 1 >> 1;
    vector<int> b = pexp(vector<int> (a.begin(), a.begin() + m));
    b.resize(n);
    psub(a, pln(b));
    add(a[0], 1);
    b = pmul(b, a);
    b.resize(n);
    return b;
  }
}
}

using ntt::pmul;
using ntt::psqr;
using ntt::pexp;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int tt;
  scanf("%d", &tt);
  vector<int> qn(tt), qm(tt);
  for (int i = 0; i < tt; ++i) {
    scanf("%d %d", &qn[i], &qm[i]);
  }
  int n = *max_element(qn.begin(), qn.end()) + 1;
  vector<int> fact(n), inv_fact(n);
  fact[0] = fact[1] = inv_fact[0] = inv_fact[1] = 1;
  for (int i = 2; i < n; ++i) {
    fact[i] = mul(fact[i - 1], i);
    inv_fact[i] = mul(md - md / i, inv_fact[md % i]);
  }
  for (int i = 2; i < n; ++i) {
    inv_fact[i] = mul(inv_fact[i - 1], inv_fact[i]);
  }
  vector<int> f(n);
  f[1] = 1;
  for (int i = 2; i < n; ++i) {
    f[i] = mul(power(i, i - 2), inv_fact[i]);
  }
  f = pexp(f);
  vector<int> g(n);
  for (int i = 2; i < n; ++i) {
    g[i] = mul(mul(md + 1 >> 1, mul(i - 1, power(i, i - 1))), inv_fact[i]);
  }
  g = pmul(g, f);
  g.resize(n);
  vector<int> base(n);
  for (int i = 1; i < n; ++i) {
    base[i] = mul(power(i, i), inv_fact[i]);
  }
  vector<int> sum = psqr(base);
  sum.resize(n);
  vector<int> sqr = pmul(sum, base);
  sqr.resize(n);
  vector<int> h(n);
  for (int i = 0; i < n; ++i) {
    h[i] = (mul(sqr[i], 2) + sum[i]) % md;
  }
  h = pmul(h, f);
  h.resize(n);
  for (int i = 0; i < n; ++i) {
    f[i] = mul(f[i], fact[i]);
    g[i] = mul(g[i], fact[i]);
    h[i] = mul(h[i], fact[i]);
  }
  for (int qq = 0; qq < tt; ++qq) {
    int n = qn[qq], m = qm[qq];
    int inside = mul(md + 1 >> 1, h[n]);
    int outside = mul(f[n], mul(md + 1 >> 1, mul(n, n - 1)));
    sub(outside, g[n]);
    outside = mul(outside, mul(m, m));
    add(inside, outside);
    inside = mul(inside, power(f[n], md - 2));
    printf("%d\n", inside);
  }
  return 0;
}

Can You Solve the Harder Problem?

考虑后缀数组求本质不同子串个数的方法,相当于求某个位置开头,某段区间结尾的区间最大值。单调栈加线段树维护即可。

#include 

using namespace std;

template<typename T> vector<int> suffix_array(const T &s, int n, int alpha) {
  vector<int> a(n);
  if (~alpha) {
    vector<int> bucket(alpha);
    for (int i = 0; i < n; ++i) {
      ++bucket[s[i]];
    }
    int sum = 0;
    for (int i = 0; i < alpha; ++i) {
      int add = bucket[i];
      bucket[i] = sum;
      sum += add;
    }
    for (int i = 0; i < n; ++i) {
      a[bucket[s[i]]++] = i;
    }
  } else {
    for (int i = 0; i < n; ++i) {
      a[i] = i;
    }
    sort(a.begin(), a.end(), [&](const int &x, const int &y) {
      return s[x] < s[y];
    });
  }
  vector<int> sorted_by_second(n);
  vector<int> ptr_group(n);
  vector<int> new_group(n);
  vector<int> group(n);
  group[a[0]] = 0;
  for (int i = 1; i < n; ++i) {
    group[a[i]] = group[a[i - 1]] + (s[a[i]] != s[a[i - 1]]);
  }
  int step = 1;
  while (group[a[n - 1]] < n - 1) {
    int ptr = 0;
    for (int i = n - step; i < n; ++i) {
      sorted_by_second[ptr++] = i;
    }
    for (int i = 0; i < n; ++i) {
      if (a[i] >= step) {
        sorted_by_second[ptr++] = a[i] - step;
      }
    }
    for (int i = n - 1; ~i; --i) {
      ptr_group[group[a[i]]] = i;
    }
    for (int i = 0; i < n; ++i) {
      int x = sorted_by_second[i];
      a[ptr_group[group[x]]++] = x;
    }
    new_group[a[0]] = 0;
    for (int i = 1; i < n; ++i) {
      if (group[a[i]] != group[a[i - 1]]) {
        new_group[a[i]] = new_group[a[i - 1]] + 1;
      } else {
        int prev = a[i - 1] + step >= n ? -1 : group[a[i - 1] + step];
        int now = a[i] + step >= n ? -1 : group[a[i] + step];
        new_group[a[i]] = new_group[a[i - 1]] + (prev != now);
      }
    }
    group = new_group;
    step <<= 1;
  }
  return a;
}

template<typename T> vector<int> build_lcp(const T &s, int n, const vector<int> &sa) {
  vector<int> pos(n);
  for (int i = 0; i < n; ++i) {
    pos[sa[i]] = i;
  }
  vector<int> lcp(n - 1);
  for (int i = 0, k = 0; i < n; ++i) {
    k = max(k - 1, 0);
    if (pos[i] == n - 1) {
      k = 0;
    } else {
      int j = sa[pos[i] + 1];
      while (i + k < n && j + k < n && s[i + k] == s[j + k]) {
        ++k;
      }
      lcp[pos[i]] = k;
    }
  }
  return lcp;
}

class segtree_t {
 public:
  struct node_t {
    long long sum = 0, tag = 0;
    
    void apply(int l, int r, long long v) {
      sum += v * (r - l + 1);
      tag += v;
    }
  };
  
  vector<node_t> tree;
  int n;

  node_t unite(const node_t &l, const node_t &r) {
    node_t result;
    result.sum = l.sum + r.sum;
    return result;
  }

  segtree_t(int n):n(n) {
    tree.resize((n << 1) - 1);
  }

  inline void pull(int x, int z) {
    tree[x] = unite(tree[x + 1], tree[z]);
  }

  inline void push(int x, int l, int r) {
    int y = l + r >> 1, z = x + (y - l + 1 << 1);
    if (tree[x].tag) {
      tree[x + 1].apply(l, y, tree[x].tag);
      tree[z].apply(y + 1, r, tree[x].tag);
      tree[x].tag = 0;
    }
  }

  void build(int x, int l, int r) {
    if (l != r) {
      int y = l + r >> 1, z = x + (y - l + 1 << 1);
      build(x + 1, l, y);
      build(z, y + 1, r);
      pull(x, z);
    }
  }

  template<typename T> void build(int x, int l, int r, const vector<T> &v) {
    if (l == r) {
      tree[x].apply(l, r, v[l]);
    } else {
      int y = l + r >> 1, z = x + (y - l + 1 << 1);
      build(x + 1, l, y, v);
      build(z, y + 1, r, v);
      pull(x, z);
    }
  }

  template<typename T> void build(const vector<T> &v) {
    build(0, 0, n - 1, v);
  }

  template<typename... T> void modify(int x, int l, int r, int ql, int qr, const T&... v) {
    if (l == ql && r == qr) {
      tree[x].apply(l, r, v...);
    } else {
      int y = l + r >> 1, z = x + (y - l + 1 << 1);
      push(x, l, r);
      if (qr <= y) {
        modify(x + 1, l, y, ql, qr, v...);
      } else if (ql > y) {
        modify(z, y + 1, r, ql, qr, v...);
      } else {
        modify(x + 1, l, y, ql, y, v...);
        modify(z, y + 1, r, y + 1, qr, v...);
      }
      pull(x, z);
    }
  }

  template<typename... T> void modify(int l, int r, const T&... v) {
    modify(0, 0, n - 1, l, r, v...);
  }

  node_t query(int x, int l, int r, int ql, int qr) {
    if (l == ql && r == qr) {
      return tree[x];
    } else {
      int y = l + r >> 1, z = x + (y - l + 1 << 1);
      push(x, l, r);
      if (qr <= y) {
        return query(x + 1, l, y, ql, qr);
      } else if (ql > y) {
        return query(z, y + 1, r, ql, qr);
      } else {
        return unite(query(x + 1, l, y, ql, y), query(z, y + 1, r, y + 1, qr));
      }
    }
  }

  node_t query(int l, int r) {
    return query(0, 0, n - 1, l, r);
  }
};

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int tt;
  scanf("%d", &tt);
  while (tt--) {
    int n;
    scanf("%d", &n);
    vector<int> a(n);
    for (int i = 0; i < n; ++i) {
      scanf("%d", &a[i]);
    }
    vector<int> sa = suffix_array(a, n, -1);
    vector<int> pos_in_sa(n);
    for (int i = 0; i < n; ++i) {
      pos_in_sa[sa[i]] = i;
    }
    vector<int> lcp = build_lcp(a, n, sa);
    segtree_t segtree(n);
    segtree.build(a);
    vector<int> st;
    st.push_back(n);
    a.push_back(1 << 30);
    long long answer = 0;
    for (int i = n - 1; ~i; --i) {
      while (a[i] >= a[st.back()]) {
        segtree.modify(st[st.size() - 1], st[st.size() - 2] - 1, a[i] - a[st.back()]);
        st.pop_back();
      }
      st.push_back(i);
      int l = i;
      if (pos_in_sa[i] != n - 1) {
        l += lcp[pos_in_sa[i]];
      }
      if (l < n) {
        answer += segtree.query(l, n - 1).sum;
      }
    }
    printf("%lld\n", answer);
  }
  return 0;
}

Distance

贪心。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int tt;
  scanf("%d", &tt);
  while (tt--) {
    int n;
    scanf("%d", &n);
    vector<int> x(n);
    for (int i = 0; i < n - 1; ++i) {
      scanf("%d", &x[i + 1]);
      x[i + 1] += x[i];
    }
    long long answer = 0;
    long long lsum = 0, rsum = 0;
    int lcnt = 0, rcnt = 0;
    for (int i = 0; i < n; ++i) {
      if (i & 1) {
        if (lcnt < rcnt) {
          answer += rsum - (long long)rcnt * x[lcnt] + (long long)lcnt * x[lcnt] - lsum;
          lsum += x[lcnt];
          ++lcnt;
        } else {
          answer += rsum - (long long)rcnt * x[n - rcnt - 1] + (long long)lcnt * x[n - rcnt - 1] - lsum;
          rsum += x[n - rcnt - 1];
          ++rcnt;
        }
      } else {
        long long lvalue = rsum - (long long)rcnt * x[lcnt] + (long long)lcnt * x[lcnt] - lsum;
        long long rvalue = rsum - (long long)rcnt * x[n - rcnt - 1] + (long long)lcnt * x[n - rcnt - 1] - lsum;
        if (lvalue > rvalue) {
          answer += lvalue;
          lsum += x[lcnt];
          ++lcnt;
        } else {
          answer += rvalue;
          rsum += x[n - rcnt - 1];
          ++rcnt;
        }
      }
      printf("%lld%c", answer, i == n - 1 ? '\n' : ' ');
    }
  }
  return 0;
}

Carpets Removal

对于每个格子,如果它被 0 0 0 块或者大于 2 2 2 块地毯覆盖,那么可以无视它。否则如果能求出覆盖它的地毯 x x x 或者 ( x , y ) (x,y) (x,y) ,那么就可以算出答案了。 x x x 可以直接用前缀和求,考虑 ( x , y ) (x,y) (x,y) ,可以记录平方和求出 x 2 + y 2 x^2+y^2 x2+y2 ,然后解出 x , y x,y x,y 即可。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int tt;
  scanf("%d", &tt);
  while (tt--) {
    int n, m;
    scanf("%d %d", &n, &m);
    vector<int> xl(n), xr(n), yl(n), yr(n);
    vector<vector<int>> cnt(m + 1, vector<int> (m + 1));
    vector<vector<int>> sum(m + 1, vector<int> (m + 1));
    vector<vector<long long>> sqr(m + 1, vector<long long> (m + 1));
    for (int i = 0; i < n; ++i) {
      scanf("%d %d %d %d", &xl[i], &xr[i], &yl[i], &yr[i]);
      --xl[i];
      --yl[i];
      ++cnt[xl[i]][yl[i]];
      ++cnt[xr[i]][yr[i]];
      --cnt[xr[i]][yl[i]];
      --cnt[xl[i]][yr[i]];
      sum[xl[i]][yl[i]] += i;
      sum[xr[i]][yr[i]] += i;
      sum[xr[i]][yl[i]] -= i;
      sum[xl[i]][yr[i]] -= i;
      sqr[xl[i]][yl[i]] += (long long)i * i;
      sqr[xr[i]][yr[i]] += (long long)i * i;
      sqr[xr[i]][yl[i]] -= (long long)i * i;
      sqr[xl[i]][yr[i]] -= (long long)i * i;
      --xr[i];
      --yr[i];
    }
    vector<pair<int, int>> all;
    vector<int> single(n);
    int free = 0;
    for (int i = 0; i < m; ++i) {
      for (int j = 0; j < m; ++j) {
        if (i) {
          cnt[i][j] += cnt[i - 1][j];
          sum[i][j] += sum[i - 1][j];
          sqr[i][j] += sqr[i - 1][j];
        }
        if (j) {
          cnt[i][j] += cnt[i][j - 1];
          sum[i][j] += sum[i][j - 1];
          sqr[i][j] += sqr[i][j - 1];
        }
        if (i && j) {
          cnt[i][j] -= cnt[i - 1][j - 1];
          sum[i][j] -= sum[i - 1][j - 1];
          sqr[i][j] -= sqr[i - 1][j - 1];
        }
        if (!cnt[i][j]) {
          ++free;
        } else if (cnt[i][j] == 1) {
          ++single[sum[i][j]];
        } else if (cnt[i][j] == 2) {
          int diff = sqrt(2 * sqr[i][j] - (long long)sum[i][j] * sum[i][j]);
          int x = sum[i][j] + diff >> 1;
          int y = sum[i][j] - diff >> 1;
          all.emplace_back(x, y);
        }
      }
    }
    int first = 0, second = 0;
    for (int i = 0; i < n; ++i) {
      second = max(second, single[i]);
      if (second > first) {
        swap(first, second);
      }
    }
    int answer = first + second;
    sort(all.begin(), all.end());
    for (int i = 0, j = 0; i < all.size(); i = j) {
      while (j < all.size() && all[j] == all[i]) {
        ++j;
      }
      answer = max(answer, single[all[i].first] + single[all[i].second] + j - i);
    }
    printf("%d\n", m * m - free - answer);
  }
  return 0;
}

Counting Failures on a Trie

哈希,二分预处理每个位置开头第一次失配的位置,然后倍增失配的过程即可。

#include 

using namespace std;

const int base = 2333;
const int md0 = 1e9 + 7;
const int md1 = 1e9 + 9;

struct hash_t {
  int hash0, hash1;

  hash_t(int hash0 = 0, int hash1 = 0): hash0(hash0), hash1(hash1) {
  }

  hash_t operator + (const int &x) const {
    return hash_t((hash0 + x) % md0, (hash1 + x) % md1);
  };

  hash_t operator * (const int &x) const {
    return hash_t((long long)hash0 * x % md0, (long long)hash1 * x % md1);
  }

  hash_t operator + (const hash_t &x) const {
    return hash_t((hash0 + x.hash0) % md0, (hash1 + x.hash1) % md1);
  };

  hash_t operator - (const hash_t &x) const {
    return hash_t((hash0 + md0 - x.hash0) % md0, (hash1 + md1 - x.hash1) % md1);
  };

  hash_t operator * (const hash_t &x) const {
    return hash_t((long long)hash0 * x.hash0 % md0, (long long)hash1 * x.hash1 % md1);
  }

  inline long long get() {
    return (long long)hash0 * md1 + hash1;
  }
};

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(0);
  cin.tie(0);
  int tt;
  cin >> tt;
  while (tt--) {
    int n, m, q;
    cin >> n >> m >> q;
    vector<vector<pair<int, char>>> adj(n + 1);
    for (int i = 1; i <= n; ++i) {
      int x;
      string s;
      cin >> x >> s;
      adj[x].emplace_back(i, s[0]);
    }
    string s;
    cin >> s;
    vector<hash_t> hash(m + 1), power(m + 1);
    power[0] = hash_t(1, 1);
    for (int i = 0; i < m; ++i) {
      power[i + 1] = power[i] * base;
      hash[i + 1] = hash[i] * base + s[i];
    }
    unordered_map<long long, int> pos;
    function<void(int, hash_t)> dfs = [&](int x, hash_t h) {
      pos[h.get()] = x;
      for (auto p : adj[x]) {
        dfs(p.first, h * base + p.second);
      }
    };
    dfs(0, hash_t(0, 0));
    int log_m = 0;
    while (1 << log_m <= m) {
      ++log_m;
    }
    auto get = [&](int l, int r) {
      return (hash[r] - hash[l] * power[r - l]).get();
    };
    vector<vector<int>> go(log_m, vector<int> (m + 1));
    for (int i = 0; i <= m; ++i) {
      int l = 0, r = m - i;
      while (l < r) {
        int mid = l + r + 1 >> 1;
        long long h = get(i, i + mid);
        if (pos.find(h) != pos.end()) {
          l = mid;
        } else {
          r = mid - 1;
        }
      }
      go[0][i] = i + l + 1;
    }
    for (int i = 1; i < log_m; ++i) {
      for (int j = 0; j <= m; ++j) {
        if (go[i - 1][j] > m) {
          go[i][j] = go[i - 1][j];
        } else {
          go[i][j] = go[i - 1][go[i - 1][j]];
        }
      }
    }
    while (q--) {
      int l, r;
      cin >> l >> r;
      --l;
      int answer = 0;
      for (int i = log_m - 1; ~i; --i) {
        if (go[i][l] <= r) {
          answer += 1 << i;
          l = go[i][l];
        }
      }
      printf("%d %d\n", answer, pos[get(l, r)]);
    }
  }
  return 0;
}

Connected Subgraphs

分类讨论一下,大概要数几个东西:

  • 正戊烷

  • 异戊烷

  • 新戊烷

  • 三元环接上一条边

  • 四元环个数

用简单容斥可以算出答案。这里难点是数四元环,考虑将边定向,从点度小的指向点度大的,那么显然一个点的出度不超过 m \sqrt m m 。假设环是 a − b − c − d a-b-c-d abcd ,且度数最大的点为 a a a ,那么枚举 c c c ,在 b b b 处将 a a a 处的标记加上 1 1 1 ,在 d d d 处加上 a a a 的标记即可。

#include 

using namespace std;

const int md = 1e9 + 7;

inline void add(int &x, int y) {
  x += y;
  if (x >= md) {
    x -= md;
  }
}

inline void sub(int &x, int y) {
  x -= y;
  if (x < 0) {
    x += md;
  }
}

inline int mul(int x, int y) {
  return (long long)x * y % md;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int tt;
  scanf("%d", &tt);
  while (tt--) {
    int n, m;
    scanf("%d %d", &n, &m);
    vector<vector<int>> adj(n), graph(n);
    for (int i = 0; i < m; ++i) {
      int x, y;
      scanf("%d %d", &x, &y);
      --x;
      --y;
      adj[x].push_back(y);
      adj[y].push_back(x);
    }
    vector<int> order(n);
    for (int i = 0; i < n; ++i) {
      order[i] = i;
    }
    sort(order.begin(), order.end(), [&](int x, int y) {
      return adj[x].size() < adj[y].size();
    });
    vector<int> pos(n);
    for (int i = 0; i < n; ++i) {
      pos[order[i]] = i;
    }
    for (int x = 0; x < n; ++x) {
      for (auto y : adj[x]) {
        if (pos[x] < pos[y]) {
          graph[x].push_back(y);
        }
      }
    }
    auto solve_a = [&]() {
      int answer = 0;
      for (int x = 0; x < n; ++x) {
        int sum = 0;
        for (auto y : adj[x]) {
          int coef = adj[y].size() - 1;
          add(answer, mul(sum, coef));
          add(sum, coef);
        }
      }
      return answer;
    };
    auto solve_b = [&]() {
      int answer = 0;
      for (int x = 0; x < n; ++x) {
        int ways = mul(md + 1 >> 1, mul(adj[x].size() - 1, adj[x].size() - 2));
        if (ways) {
          for (auto y : adj[x]) {
            add(answer, mul(adj[y].size() - 1, ways));
          }
        }
      }
      return answer;
    };
    auto solve_c = [&]() {
      int answer = 0;
      for (int x = 0; x < n; ++x) {
        int coef = 41666667;
        for (int i = 0; i < 4; ++i) {
          coef = mul(coef, adj[x].size() - i);
        }
        add(answer, coef);
      }
      return answer;
    };
    auto solve_d = [&]() {
      vector<bool> connect(n);
      int answer = 0;
      for (int x = 0; x < n; ++x) {
        for (auto y : graph[x]) {
          connect[y] = true;
        }
        for (auto y : graph[x]) {
          for (auto z : graph[y]) {
            if (connect[z]) {
              add(answer, adj[x].size() + adj[y].size() + adj[z].size() - 5);
            }
          }
        }
        for (auto y : graph[x]) {
          connect[y] = false;
        }
      }
      return answer;
    };
    auto solve_e = [&]() {
      vector<int> value(n);
      int answer = 0;
      for (int x = 0; x < n; ++x) {
        for (auto y : adj[x]) {
          for (auto z : graph[y]) {
            if (pos[z] > pos[x]) {
              add(answer, value[z]);
              ++value[z];
            }
          }
        }
        for (auto y : adj[x]) {
          for (auto z : graph[y]) {
            if (pos[z] > pos[x]) {
              value[z] = 0;
            }
          }
        }
      }
      return answer;
    };
    int answer = 0;
    add(answer, solve_a());
    add(answer, solve_b());
    add(answer, solve_c());
    sub(answer, mul(3, solve_d()));
    sub(answer, mul(3, solve_e()));
    printf("%d\n", answer);
  }
  return 0;
}

你可能感兴趣的:(CodeForces Gym 102028 简要题解)