CodeForces Gym 101981 简要题解

Adrien and Austin

特判 n = 0 n=0 n=0 或者 k = 1 k=1 k=1 的情况,当 k > 1 k>1 k>1 的时候,先手取中间,然后后手不管取什么先手取相对的,所以先手必胜。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n, m;
  scanf("%d %d", &n, &m);
  if (m == 1) {
    puts(n & 1 ? "Adrien" : "Austin");
  } else {
    puts(n ? "Adrien" : "Austin");
  }
  return 0;
}

Tournament

凸优化+决策单调性DP。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n, m;
  scanf("%d %d", &n, &m);
  vector<long long> sum(n + 1);
  vector<long long> f(n + 1);
  vector<int> g(n + 1);
  for (int i = 1; i <= n; ++i) {
    scanf("%lld", &sum[i]);
    sum[i] += sum[i - 1];
  }
  vector<pair<int, int>> q(n);
  auto solve = [&](long long cost) {
    int ll = 0, rr = 0;
    auto get = [&](int l, int r) {
      return f[l] + cost + sum[l] + sum[r] - sum[l + r >> 1] - sum[l + r + 1 >> 1];
    };
    for (int i = 0; i < n; ++i) {
      while (ll < rr && get(i, q[rr - 1].second) < get(q[rr - 1].first, q[rr - 1].second)) {
        --rr;
      }
      if (ll == rr) {
        q[rr++] = make_pair(i, i + 1);
      } else {
        int l = q[rr - 1].second, r = n + 1;
        while (l < r) {
          int mid = l + r >> 1;
          if (get(i, mid) < get(q[rr - 1].first, mid)) {
            r = mid;
          } else {
            l = mid + 1;
          }
        }
        if (r <= n) {
          q[rr++] = make_pair(i, r);
        }
      }
      while (ll + 1 < rr && q[ll + 1].second <= i + 1) {
        ++ll;
      }
      f[i + 1] = get(q[ll].first, i + 1);
      g[i + 1] = g[q[ll].first] + 1;
    }
  };
  long long l = 0, r = sum[n];
  while (l < r) {
    long long mid = l + r >> 1;
    solve(mid);
    if (g[n] <= m) {
      r = mid;
    } else {
      l = mid + 1;
    }
  }
  solve(r);
  printf("%lld\n", f[n] - r * m);
  return 0;
}

Cherry and Chocolate

考虑第一个人选了一个点之后,第二个人一定会选择某个子树的重心,然后第一个人删去最大的那个子树。这时候我们求出了一个最优解,假设第一个人选的点不在第二个人当前这个解所在子树内,那么第二个人保持同样的决策答案不会对第一个人更优。每次选重心之后朝一个子树递归即可。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  vector<vector<int>> adj(n);
  for (int i = 0; i < n - 1; ++i) {
    int x, y;
    scanf("%d %d", &x, &y);
    --x;
    --y;
    adj[x].push_back(y);
    adj[y].push_back(x);
  }
  int answer = 0;
  vector<int> size(n), parent(n);
  vector<bool> visit(n);
  auto find_centroid = [&](int x, bool ban) {
    vector<int> order(1, x);
    for (int i = 0; i < order.size(); ++i) {
      int x = order[i];
      for (auto y : adj[x]) {
        if ((!visit[y] || !ban) && y != parent[x]) {
          order.push_back(y);
          parent[y] = x;
        }
      }
    }
    int root = -1, value = n;
    for (int i = order.size() - 1; ~i; --i) {
      int x = order[i], subtree = 0;
      size[x] = 1;
      for (auto y : adj[x]) {
        if ((!visit[y] || !ban) && y != parent[x]) {
          size[x] += size[y];
          subtree = max(subtree, size[y]);
        }
      }
      subtree = max(subtree, (int)order.size() - size[x]);
      if (subtree < value) {
        root = x;
        value = subtree;
      }
    }
    return make_pair(root, value + n - (int)order.size());
  };
  auto get = [&](int x) {
    pair<int, int> result = make_pair(n, -1);
    for (auto y : adj[x]) {
      parent[y] = x;
      result = min(result, make_pair(find_centroid(y, false).second, y));
    }
    return result;
  };
  int x = 0;
  while (!visit[x]) {
    parent[x] = -1;
    x = find_centroid(x, true).first;
    visit[x] = true;
    pair<int, int> result = get(x);
    answer = max(answer, result.first);
    x = result.second;
  }
  printf("%d\n", answer);
  return 0;
}

Country Meow

三分套三分套三分。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  vector<vector<int>> a(3, vector<int> (n));
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < 3; ++j) {
      scanf("%d", &a[j][i]);
    }
  }
  vector<double> p(3);
  function<double(int)> ternary_search = [&](int x) {
    if (x == 3) {
      double answer = 0;
      for (int i = 0; i < n; ++i) {
        double result = 0;
        for (int j = 0; j < 3; ++j) {
          result += (p[j] - a[j][i]) * (p[j] - a[j][i]);
        }
        answer = max(answer, result);
      }
      return answer;
    } else {
      double l = *min_element(a[x].begin(), a[x].end());
      double r = *max_element(a[x].begin(), a[x].end());
      double answer = 1e18;
      for (int tt = 0; tt < 50; ++tt) {
        double ll = l + (r - l) / 3, rr = r - (r - l) / 3, result_l, result_r;
        p[x] = ll;
        result_l = ternary_search(x + 1);
        p[x] = rr;
        result_r = ternary_search(x + 1);
        answer = min(answer, min(result_l, result_r));
        if (result_l < result_r) {
          r = rr;
        } else {
          l = ll;
        }
      }
      return answer;
    }
  };
  printf("%lf\n", sqrt(ternary_search(0)));
  return 0;
}

Eva and Euro coins

注意到可以进行一个操作就是把一个 1 1 1 往前推 k k k 步,然后如果有连续的 k k k 1 1 1 就消掉,搞个最小表示即可。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  int n, m;
  cin >> n >> m;
  string s, t;
  cin >> s >> t;
  auto get = [&](string s) {
    vector<pair<int, int>> p(1, make_pair(-1, 0));
    for (int i = 0; i < n; ++i) {
      if (s[i] == '1') {
        int x = i % m + (p.back().first + 1) / m * m;
        if (x <= p.back().first) {
          x += m;
        }
        p.emplace_back(x, x == p.back().first + 1 ? p.back().second + 1 : 1);
        if (p.back().second == m) {
          for (int j = 0; j < m; ++j) {
            p.pop_back();
          }
        }
      }
    }
    string result(n, '0');
    for (int i = 1; i < p.size(); ++i) {
      result[p[i].first] = '1';
    }
    return result;
  };
  cout << (get(s) == get(t) ? "Yes" : "No") << endl;
  return 0;
}

Frank

f ( i , j ) f(i,j) f(i,j) 表示从 i i i 走到 j j j 的期望步数, g ( i ) g(i) g(i) 表示 i i i 走一圈回到 i i i 的期望步数, e ( i , j ) e(i,j) e(i,j) 表示从 i i i 走一步走到 j j j 的概率,那么 f ( i , j ) = ∑ k e ( i , k ) f ( k , j ) + 1 − [ i = j ] g ( i ) f(i,j) = \sum_{k} e(i,k)f(k,j) + 1 - [i=j]g(i) f(i,j)=ke(i,k)f(k,j)+1[i=j]g(i) 。注意到 g ( i ) = 1 P ( i ) g(i) = \frac{1}{P(i)} g(i)=P(i)1 ,其中 P ( i ) P(i) P(i) 表示无限时刻后停留在 i i i 的概率,这个可以通过高消求出。那么现在就是解一个方程 A X = B AX = B AX=B ,由于 A A A 的秩为 n − 1 n-1 n1 ,不能直接解,但可以发现 X i , i = 0 X_{i,i} = 0 Xi,i=0 ,可以求出一组特解之后用 X i , i X_{i,i} Xi,i 来调整。

#include 

using namespace std;

typedef double ld;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n, m, q;
  scanf("%d %d %d", &n, &m, &q);
  vector<vector<ld>> adj(n, vector<ld> (n));
  vector<int> degree(n);
  while (m--) {
    int x, y;
    scanf("%d %d", &x, &y);
    ++adj[x][y];
    ++degree[x];
  }
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < n; ++j) {
      adj[i][j] /= degree[i];
    }
  }
  vector<vector<ld>> a(n, vector<ld> (n + 1));
  for (int i = 0; i <= n; ++i) {
    a[0][i] = 1;
  }
  for (int i = 1; i < n; ++i) {
    a[i][i] = 1;
    for (int j = 0; j < n; ++j) {
      a[i][j] -= adj[j][i];
    }
  }
  for (int i = 0; i < n; ++i) {
    int j = i;
    while (j < n && !a[j][i]) {
      ++j;
    }
    if (j != i) {
      for (int k = i; k <= n; ++k) {
        swap(a[i][k], a[j][k]);
      }
    }
    for (int j = 0; j < n; ++j) {
      if (j != i && a[j][i]) {
        ld coef = a[j][i] / a[i][i];
        for (int k = i; k <= n; ++k) {
          a[j][k] -= a[i][k] * coef;
        }
      }
    }
  }
  vector<ld> g(n);
  for (int i = 0; i < n; ++i) {
    g[i] = a[i][i] / a[i][n];
  }
  a = vector<vector<ld>> (n, vector<ld> (n << 1));
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < n; ++j) {
      a[i][j] = (i == j) - adj[i][j];
      a[i][j + n] = 1;
    }
    a[i][i + n] -= g[i];
  }
  for (int i = 0; i < n - 1; ++i) {
    int j = i;
    while (j < n && !a[j][i]) {
      ++j;
    }
    if (j != i) {
      for (int k = i; k < n << 1; ++k) {
        swap(a[i][k], a[j][k]);
      }
    }
    ld coef = a[i][i];
    for (int j = i; j < n << 1; ++j) {
      a[i][j] /= coef;
    }
    for (int j = 0; j < n; ++j) {
      if (j != i && a[j][i]) {
        ld coef = a[j][i];
        for (int k = i; k < n << 1; ++k) {
          a[j][k] -= a[i][k] * coef;
        }
      }
    }
  }
  vector<vector<ld>> f(n, vector<ld> (n));
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < n; ++j) {
      f[i][j] = a[i][j + n] - a[j][j + n];
    }
  }
  while (q--) {
    int total, current;
    scanf("%d %d", &total, &current);
    if (total == 1) {
      puts("1");
    } else {
      ld answer = 0;
      for (int i = 0; i < total - 1; ++i) {
        int next;
        scanf("%d", &next);
        answer += f[current][next];
        current = next;
      }
      printf("%.9lf\n", answer);
    }
  }
  return 0;
}

Pyramid

枚举每个三角形的bounding-box,相当于每个边界与外边界平行的三角形有边长的贡献,化简一下式子,答案是 ( n + 3 4 ) \binom{n+3}{4} (4n+3)

#include 

using namespace std;

const int md = 1e9 + 7;

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;
    scanf("%d", &n);
    printf("%d\n", mul(mul(mul(mul(n, n + 1), n + 2), n + 3), (md + 1) / 24));
  }
  return 0;
}

Huge Discount

如果区间内众数出现次数大于区间长度的一半,那么可以把其他数全消掉;如果区间长度为偶数可以全消掉;否则会剩下一个数,枚举剩下的数是什么,限制大概是个偏序关系,树状数组维护即可。

#include 

using namespace std;

template<typename T> class fenwick_t {
 public:
  vector<T> fenw;
  int n;

  fenwick_t(int n):n(n) {
    fenw.resize(n);
  }

  void modify(int x, T value) {
    while (x < n) {
      fenw[x] += value;
      x |= x + 1;
    }
  }

  T query(int x) {
    T result{};
    while (x >= 0) {
      result += fenw[x];
      x = (x & x + 1) - 1;
    }
    return result;
  }
};

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  string s;
  cin >> n >> s;
  vector<vector<int>> sum(3, vector<int> (n + 1));
  vector<vector<fenwick_t<int>>> fenwick(3, vector<fenwick_t<int>> (3, n));
  vector<int> max_value(3), available(3);
  for (int i = 0; i < 3; ++i) {
    for (int j = n - 1; ~j; --j) {
      sum[i][j] = sum[i][j + 1] + (s[j] == i + '0' ? 1 : -1);
    }
    max_value[i] = *max_element(sum[i].begin(), sum[i].begin() + n);
  }
  vector<int> tag(n + 1);
  for (int i = n - 1; ~i; --i) {
    bool already = false;
    for (int j = 0; j < 3; ++j) {
      if (sum[j][i] > 0) {
        tag[0] += j;
        tag[sum[j][i]] -= j;
        already = true;
      }
    }
    if (n - i & 1) {
      bool can = true;
      for (int j = 0; j < 3; ++j) {
        if (sum[j][i + 1] > 0) {
          can = false;
        }
      }
      if (can) {
        ++available[s[i] - '0'];
        for (int j = 0; j < 3; ++j) {
          fenwick[s[i] - '0'][j].modify(max_value[j] - sum[j][i], 1);
        }
      }
      if (!already) {
        for (int j = 0; j < 3; ++j) {
          int cnt = 0;
          for (int k = 0; k < 3; ++k) {
            cnt += fenwick[j][k].query(max_value[k] - sum[k][i]);
          }
          if (cnt != available[j]) {
            tag[0] += j;
            tag[1] -= j;
            break;
          }
        }
      }
    }
  }
  for (int i = 1; i <= n; ++i) {
    tag[i] += tag[i - 1];
  }
  for (int i = 0; i < tag.size(); ++i) {
    if (tag[i] >= 10) {
      if (i == tag.size() - 1) {
        tag.push_back(0);
      }
      tag[i + 1] += tag[i] / 10;
      tag[i] %= 10;
    }
  }
  while (!tag.empty() && !tag.back()) {
    tag.pop_back();
  }
  if (tag.empty()) {
    puts("0");
  } else {
    for (int i = tag.size() - 1; ~i; --i) {
      putchar(tag[i] + '0');
    }
    putchar(10);
  }
  return 0;
}

Magic Potion

网络流。

#include 

using namespace std;

const int inf = 0x3f3f3f3f;

namespace flow {
struct edge_t {
  int to, cap, rev;

  edge_t(int t, int c, int r) {
    to = t;
    cap = c;
    rev = r;
  }
};

int n, source, sink, answer;
vector<vector<edge_t>> adj;
vector<int> dist, current;

void init(int v, int s, int t) {
  n = v;
  source = s;
  sink = t;
  answer = 0;
  adj.clear();
  adj.resize(n);
  dist.resize(n);
  current.resize(n);
}

void add(int x, int y, int c) {
  adj[x].push_back(edge_t(y, c, adj[y].size()));
  adj[y].push_back(edge_t(x, 0, adj[x].size() - 1));
}

bool bfs() {
  queue<int> q;
  for (int i = 0; i < n; ++i) {
    dist[i] = -1;
  }
  dist[source] = 0;
  q.push(source);
  while (!q.empty()) {
    int x = q.front();
    q.pop();
    for (auto e : adj[x]) {
      if (e.cap && !~dist[e.to]) {
        dist[e.to] = dist[x] + 1;
        if (e.to == sink) {
          return true;
        }
        q.push(e.to);
      }
    }
  }
  return false;
}

int dfs(int x, int f) {
  if (x == sink) {
    return f;
  }
  for (int &i = current[x]; ~i; --i) {
    edge_t &e = adj[x][i];
    if (e.cap && dist[e.to] == dist[x] + 1) {
      int w = dfs(e.to, min(e.cap, f));
      if (w) {
        e.cap -= w;
        adj[e.to][e.rev].cap += w;
        return w;
      }
    }
  }
  return 0;
}

int max_flow() {
  while (bfs()) {
    for (int i = 0; i < n; ++i) {
      current[i] = adj[i].size() - 1;
    }
    while (true) {
      int flow = dfs(source, inf);
      if (!flow) {
        break;
      }
      answer += flow;
    }
  }
  return answer;
}
}

using flow::source;
using flow::sink;
using flow::init;
using flow::add;
using flow::max_flow;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n, m, q;
  scanf("%d %d %d", &n, &m, &q);
  init(n + m + 3, n + m + 1, n + m + 2);
  add(source, n + m, q);
  for (int i = 0; i < n; ++i) {
    add(source, i, 1);
    add(n + m, i, 1);
  }
  for (int i = 0; i < m; ++i) {
    add(i + n, sink, 1);
  }
  for (int i = 0; i < n; ++i) {
    int size;
    scanf("%d", &size);
    while (size--) {
      int x;
      scanf("%d", &x);
      add(i, x - 1 + n, 1);
    }
  }
  printf("%d\n", max_flow());
  return 0;
}

Prime Game

对每个质数求出上一次出现的位置,然后随便算算答案就行了。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  vector<int> a(n);
  for (int i = 0; i < n; ++i) {
    scanf("%d", &a[i]);
  }
  int m = *max_element(a.begin(), a.end());
  vector<int> minp(m + 1);
  vector<int> primes;
  for (int i = 2; i <= m; ++i) {
    if (!minp[i]) {
      minp[i] = i;
      primes.push_back(i);
    }
    for (auto p : primes) {
      if (i * p > m) {
        break;
      }
      minp[i * p] = p;
      if (i % p == 0) {
        break;
      }
    }
  }
  vector<int> last(m + 1, -1);
  long long answer = 0;
  for (int i = 0; i < n; ++i) {
    for (int x = a[i]; x != 1; ) {
      int p = minp[x];
      while (x % p == 0) {
        x /= p;
      }
      answer += (long long)(n - i) * (i - last[p]);
      last[p] = i;
    }
  }
  printf("%lld\n", answer);
  return 0;
}

Kangaroo Puzzle

每次合并两个即可。

#include 

using namespace std;

const vector<int> dx = {-1, 1, 0, 0};
const vector<int> dy = {0, 0, -1, 1};
const string direction = "UDLR";

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n, m;
  cin >> n >> m;
  vector<string> board(n);
  for (int i = 0; i < n; ++i) {
    cin >> board[i];
  }
  vector<pair<int, int>> all;
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < m; ++j) {
      if (board[i][j] == '1') {
        all.emplace_back(i, j);
      }
    }
  }
  auto bfs = [&](pair<int, int> from, pair<int, int> to) {
    vector<vector<int>> prev(n, vector<int> (m, -1));
    queue<pair<int, int>> q;
    prev[from.first][from.second] = 4;
    q.push(from);
    while (!q.empty()) {
      int x = q.front().first, y = q.front().second;
      q.pop();
      for (int i = 0; i < 4; ++i) {
        int xx = x + dx[i], yy = y + dy[i];
        if (xx >= 0 && xx < n && yy >= 0 && yy < m && board[xx][yy] == '1' && !~prev[xx][yy]) {
          prev[xx][yy] = i ^ 1;
          q.emplace(xx, yy);
        }
      }
    }
    return prev[to.first][to.second];
  };
  for (int i = 0; i < all.size(); ++i) {
    while (all[i] != all[0]) {
      int d = bfs(all[i], all[0]);
      putchar(direction[d]);
      for (auto &p : all) {
        int x = p.first + dx[d], y = p.second + dy[d];
        if (x >= 0 && x < n && y >= 0 && y < m && board[x][y] == '1') {
          p = make_pair(x, y);
        }
      }
    }
  }
  putchar(10);
  return 0;
}

Lagrange the Chef

特判掉一些情况之后,假设最后会保留一个 X X X ,保留一个 Y Y Y ,记 d p ( i , j , s , l a s t ) dp(i,j,s,last) dp(i,j,s,last) 表示考虑前 i i i 个数,当前取出的不是 X X X 也不是 Y Y Y 的数减去 X Y XY XY 相邻的对数为 j j j ,是否保留了 X X X Y Y Y ,以及上一个在序列中的数,可以 O ( 1 ) O(1) O(1) 转移。

#include 

using namespace std;

const int inf = 0x3f3f3f3f;

template<typename T> inline void cmin(T &x, const T &y) {
  x = min(x, y);
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n, x, y;
  scanf("%d %d %d", &n, &x, &y);
  vector<int> a(n), c(3);
  for (int i = 0; i < n; ++i) {
    int z;
    scanf("%d", &z);
    if (z == x) {
      a[i] = 0;
    } else if (z == y) {
      a[i] = 1;
    } else {
      a[i] = 2;
    }
    ++c[a[i]];
  }
  if (!c[0] || !c[1]) {
    puts("0");
    return 0;
  }
  if (!c[2]) {
    puts("-1");
    return 0;
  }
  int answer = n;
  for (int remove = 0; remove < 2; ++remove) {
    bool flag = a[0] == 2 || a[n - 1] == 2;
    int last = 2;
    for (int i = 0; i < n; ++i) {
      if (a[i] != remove) {
        if (a[i] == 2 && last == 2) {
          flag = true;
        }
        last = a[i];
      }
    }
    if (last == 2) {
      flag = true;
    }
    if (flag) {
      answer = min(answer, c[remove]);
    } else {
      answer = min(answer, c[remove] + 1);
    }
  }
  vector<vector<int>> f(n + 1, vector<int> (12, inf));
  f[c[0] + c[1]][2] = 0;
  for (int i = 0; i < n; ++i) {
    vector<vector<int>> g(n + 1, vector<int> (12, inf));
    for (int j = 0; j <= n; ++j) {
      for (int state = 0; state < 4; ++state) {
        for (int last = 0; last < 3; ++last) {
          if (f[j][state * 3 + last] != inf) {
            if (a[i] == 2) {
              cmin(g[j + 1][state * 3 + last], f[j][state * 3 + last] + 1);
              cmin(g[j][state * 3 + 2], f[j][state * 3 + last]);
            } else {
              cmin(g[j][state * 3 + last], f[j][state * 3 + last] + 1);
              cmin(g[j - (last + a[i] == 1)][(state | 1 << a[i]) * 3 + a[i]], f[j][state * 3 + last]);
            }
          }
        }
      }
    }
    f = g;
  }
  for (int i = c[0] + c[1]; i <= n; ++i) {
    for (int j = 9; j < 12; ++j) {
      answer = min(answer, f[i][j]);
    }
  }
  printf("%d\n", answer);
  return 0;
}

Mediocre String Problem

s s s 翻转,枚举分界点,前一部分用 manacher 预处理,后一部分用 z-algorithm 预处理即可。

#include 

using namespace std;

template<typename T> vector<int> z_algorithm(const T &a) {
  int n = a.size();
  vector<int> z(n);
  z[0] = n;
  int l = -1, r = -1;
  for (int i = 1; i < n; ++i) {
    z[i] = i >= r ? 0 : min(r - i, z[i - l]);
    while (i + z[i] < n && a[i + z[i]] == a[z[i]]) {
      ++z[i];
    }
    if (i + z[i] > r) {
      l = i;
      r = i + z[i];
    }
  }
  return z;
}

template<typename T> vector<int> manacher(const T &a) {
  int n = a.size(), m = (n << 1) - 1;
  T b(m, -1);
  vector<int> p(m, 0);
  for (int i = 0; i < n; ++i) {
    b[i << 1] = a[i];
  }
  int x = 0;
  for (int i = 1; i < m; ++i) {
    if (i <= x + p[x]) {
      p[i] = min(p[(x << 1) - i], x + p[x] - i);
    }
    while (i - p[i] - 1 >= 0 && i + p[i] + 1 < m && b[i - p[i] - 1] == b[i + p[i] + 1]) {
      ++p[i];
    }
    if (i + p[i] >= x + p[x]) {
      x = i;
    }
  }
  for (int i = 0; i < m; ++i) {
    if (i - p[i] == 0 || i + p[i] == m - 1) {
      ++p[i];
    }
  }
  for (int i = 0; i < m; ++i) {
    p[i] >>= 1;
  }
  return p;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  string s, t;
  cin >> s >> t;
  reverse(s.begin(), s.end());
  vector<int> z = z_algorithm(t + '#' + s);
  vector<int> p = manacher(s);
  vector<int> tag(s.length() + 1);
  for (int i = 0; i < p.size(); ++i) {
    ++tag[i + 1 >> 1];
    --tag[(i >> 1) + p[i] + 1];
  }
  for (int i = 0; i < s.length(); ++i) {
    tag[i + 1] += tag[i];
  }
  long long answer = 0;
  for (int i = 0; i < s.length() - 1; ++i) {
    answer += (long long)z[i + t.length() + 2] * tag[i];
  }
  printf("%lld\n", answer);
  return 0;
}

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