CodeForces 1045 简要题解

Last chance

线段树优化连边,输出方案先输出容量为 2 2 2 的。

#include <bits/stdc++.h>

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].emplace_back(y, c, adj[y].size());
  adj[y].emplace_back(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;
using flow::adj;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n, m;
  scanf("%d %d", &n, &m);
  init((m << 1) - 1 + n + 2, (m << 1) - 1 + n, (m << 1) - 1 + n + 1);
  vector<int> id(m);
  vector<int> related((m << 1) - 1);

  function<void(int, int, int)> build = [&](int x, int l, int r) {
    if (l == r) {
      id[l] = x;
      related[x] = l;
    } else {
      int y = l + r >> 1, z = x + (y - l + 1 << 1);
      build(x + 1, l, y);
      build(z, y + 1, r);
      add(x, x + 1, inf);
      add(x, z, inf);
    }
  };

  function<void(int, int, int, int, int, int)> query = [&](int x, int l, int r, int ql, int qr, int from) {
    if (l == ql && r == qr) {
      add(from, x, 1);
    } else {
      int y = l + r >> 1, z = x + (y - l + 1 << 1);
      if (qr <= y) {
        query(x + 1, l, y, ql, qr, from);
      } else if (ql > y) {
        query(z, y + 1, r, ql, qr, from);
      } else {
        query(x + 1, l, y, ql, y, from);
        query(z, y + 1, r, y + 1, qr, from);
      }
    }
  };

  build(0, 0, m - 1);
  for (int i = 0; i < m; ++i) {
    add(id[i], sink, 1);
  }
  vector<int> type(n), a(n), b(n), c(n);
  for (int i = 0; i < n; ++i) {
    scanf("%d", &type[i]);
    if (!type[i]) {
      add(source, i + (m << 1) - 1, 1);
      int size;
      scanf("%d", &size);
      while (size--) {
        int x;
        scanf("%d", &x);
        --x;
        add(i + (m << 1) - 1, id[x], 1);
      }
    } else if (type[i] == 1) {
      scanf("%d %d", &a[i], &b[i]);
      --a[i];
      --b[i];
      add(source, i + (m << 1) - 1, 1);
      query(0, 0, m - 1, a[i], b[i], i + (m << 1) - 1);
    } else {
      scanf("%d %d %d", &a[i], &b[i], &c[i]);
      --a[i];
      --b[i];
      --c[i];
      add(source, i + (m << 1) - 1, 2);
      add(i + (m << 1) - 1, id[a[i]], 1);
      add(i + (m << 1) - 1, id[b[i]], 1);
      add(i + (m << 1) - 1, id[c[i]], 1);
    }
  }
  printf("%d\n", max_flow());
  vector<bool> use(m);
  for (int i = 0; i < m; ++i) {
    for (auto e : adj[id[i]]) {
      if (e.to == sink && !e.cap) {
        use[i] = true;
      }
    }
  }
  for (int i = 0; i < n; ++i) {
    if (type[i] == 2) {
      bool found = false;
      for (auto e : adj[i + (m << 1) - 1]) {
        if (e.to == source && e.cap) {
          found = true;
          break;
        }
      }
      if (found) {
        int remain = 2;
        for (auto e : adj[i + (m << 1) - 1]) {
          if (e.to != source && !e.cap) {
            printf("%d %d\n", i + 1, related[e.to] + 1);
            --remain;
            use[related[e.to]] = false;
          }
        }
        if (remain && use[a[i]]) {
          printf("%d %d\n", i + 1, a[i] + 1);
          --remain;
          use[a[i]] = false;
        }
        if (remain && use[b[i]]) {
          printf("%d %d\n", i + 1, b[i] + 1);
          --remain;
          use[b[i]] = false;
        }
        if (remain && use[c[i]]) {
          printf("%d %d\n", i + 1, c[i] + 1);
          --remain;
          use[c[i]] = false;
        }
      }
    }
  }
  for (int i = 0; i < n; ++i) {
    if (!type[i]) {
      for (auto e : adj[i + (m << 1) - 1]) {
        if (e.to != source && !e.cap && use[related[e.to]]) {
          printf("%d %d\n", i + 1, related[e.to] + 1);
          use[related[e.to]] = false;
        }
      }
    }
  }
  for (int i = 0; i < m; ++i) {
    if (use[i]) {
      int p = -1;
      for (int j = 0; j < n; ++j) {
        if (type[j] == 1 && a[j] <= i && b[j] >= i && (!~p || b[j] < b[p])) {
          p = j;
        }
      }
      type[p] = -1;
      printf("%d %d\n", p + 1, i + 1);
    }
  }
  return 0;
}

Space Isaac

如果一个数不能被表示那么它减去 a i a_i ai 仍然在 a a a 中。

假设这个数是 a 0 + a i a_0 + a_i a0+ai ,不难发现需要满足 a 0 + a i = a 1 + a i − 1 = a 2 + a i − 2 ⋯ a_0 + a_i = a_1 + a_{i-1} = a_2 + a_{i-2}\cdots a0+ai=a1+ai1=a2+ai2 。差分之后相当于判两部分是不是回文。

#include <bits/stdc++.h>

using namespace std;

vector<int> manacher(vector<int> a) {
  int n = a.size(), m = (n << 1) - 1;
  vector<int> b(m, -1), 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
  int n, m;
  scanf("%d %d", &n, &m);
  vector<int> a(n);
  for (int i = 0; i < n; ++i) {
    scanf("%d", &a[i]);
  }
  vector<int> b(n);
  for (int i = 0; i < n - 1; ++i) {
    b[i] = a[i + 1] - a[i];
  }
  b[n - 1] = (a[0] + m - a[n - 1]) % m;
  vector<int> p = manacher(b);
  vector<int> answer;
  
  auto palindrome = [&](int l, int r) {
    return l == r || p[l + r - 1] >= r - l >> 1;
  };

  for (int i = 0; i < n; ++i) {
    if (palindrome(0, i) && palindrome(i, n)) {
      answer.push_back((a[0] + a[i]) % m);
    }
  }
  printf("%d\n", answer.size());
  if (!answer.empty()) {
    vector<int> temp;
    int p = min_element(answer.begin(), answer.end()) - answer.begin();
    for (int j = p; j < answer.size(); ++j) {
      temp.push_back(answer[j]);
    }
    for (int j = 0; j < p; ++j) {
      temp.push_back(answer[j]);
    }
    for (int j = 0; j < temp.size(); ++j) {
      printf("%d%c", temp[j], j == temp.size() - 1 ? '\n' : ' ');
    }
  }
  return 0;
}

Hyperspace Highways

建个圆方树。

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n, m, q;
  scanf("%d %d %d", &n, &m, &q);
  vector<vector<int>> adj(n), graph(n);
  for (int i = 0; i < m; ++i) {
    int x, y;
    scanf("%d %d", &x, &y);
    --x;
    --y;
    graph[x].push_back(y);
    graph[y].push_back(x);
  }

  vector<int> dfn(n, -1), low(n, -1), stack;
  int dfn_t = 0;
  m = n;
  function<void(int, int)> tarjan = [&](int x, int parent) {
    dfn[x] = low[x] = dfn_t++;
    stack.push_back(x);
    for (auto y : graph[x]) {
      if (y != parent) {
        if (!~dfn[y]) {
          tarjan(y, x);
          low[x] = min(low[x], low[y]);
          if (dfn[x] <= low[y]) {
            adj.push_back(vector<int>());
            adj[x].push_back(m);
            int z;
            do {
              z = stack.back();
              stack.pop_back();
              adj[m].push_back(z);
            } while (z != y);
            ++m;
          }
        } else {
          low[x] = min(low[x], dfn[y]);
        }
      }
    }
  };

  tarjan(0, -1);
  int log_m = 0;
  while (1 << log_m < m) {
    ++log_m;
  }
  vector<vector<int>> ancestor(log_m, vector<int> (m, -1));
  vector<int> depth(m), dist(m);

  function<void(int)> dfs = [&](int x) {
    for (int i = 1; depth[x] >> i; ++i) {
      ancestor[i][x] = ancestor[i - 1][ancestor[i - 1][x]];
    }
    for (auto y : adj[x]) {
      ancestor[0][y] = x;
      depth[y] = depth[x] + 1;
      dist[y] = dist[x] + 1 - (y >= n);
      dfs(y);
    }
  };

  auto lca = [&](int x, int y) {
    if (depth[x] < depth[y]) {
      swap(x, y);
    }
    for (int i = 0; depth[x] > depth[y]; ++i) {
      if (depth[x] - depth[y] >> i & 1) {
        x = ancestor[i][x];
      }
    }
    if (x == y) {
      return x;
    }
    for (int i = log_m - 1; ~i; --i) {
      if (ancestor[i][x] != ancestor[i][y]) {
        x = ancestor[i][x];
        y = ancestor[i][y];
      }
    }
    return ancestor[0][x];
  };

  dfs(0);
  while (q--) {
    int x, y;
    scanf("%d %d", &x, &y);
    --x;
    --y;
    int z = lca(x, y);
    printf("%d\n", dist[x] + dist[y] - (dist[z] << 1) - (z >= n));
  }
  return 0;
}

Interstellar battle

期望等于每个点存在的概率减去每条边两个点都存在的概率。

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  vector<double> prob(n), sum(n);
  for (int i = 0; i < n; ++i) {
    scanf("%lf", &prob[i]);
    prob[i] = 1 - prob[i];
  }
  vector<vector<int>> adj(n);
  for (int i = 0; i < n - 1; ++i) {
    int x, y;
    scanf("%d %d", &x, &y);
    adj[x].push_back(y);
    adj[y].push_back(x);
  }

  vector<int> parent(n, -1);
  function<void(int)> dfs = [&](int x) {
    for (auto y : adj[x]) {
      if (y != parent[x]) {
        sum[x] += prob[y];
        parent[y] = x;
        dfs(y);
      }
    }
  };
  dfs(0);

  double answer = 0;
  for (int i = 0; i < n; ++i) {
    answer += prob[i] * (1 - sum[i]);
  }

  int m;
  scanf("%d", &m);
  while (m--) {
    int x;
    double delta;
    scanf("%d %lf", &x, &delta);
    delta = 1 - delta - prob[x];
    answer += delta * (1 - sum[x]);
    prob[x] += delta;
    if (x) {
      answer -= prob[parent[x]] * delta;
      sum[parent[x]] += delta;
    }
    printf("%lf\n", answer);
  }
  return 0;
}

Ancient civilizations

原题链接

如果凸包上颜色超过 2 2 2 段则无解,否则可以用一个类似三角剖分的过程来解决:

定义 s o l v e ( a , b , c ) solve(a, b, c) solve(a,b,c) 表示 a a a 是一种颜色, b , c b, c b,c 是一种颜色并且有边 ( b , c ) (b, c) (b,c) ,然后要连上三角形内部的所有边,找到一个与 a a a 同色的点 d d d ,连接 ( a , d ) (a, d) (a,d) ,递归计算,如果不存在直接所有内部点都往 c c c 连就行了。

#include <bits/stdc++.h>

using namespace std;

struct point_t {
  int x, y, id, color;

  point_t() {
  }

  point_t(int x, int y):x(x), y(y) {
  }

  bool operator < (const point_t &b) const {
    return x < b.x || (x == b.x && y < b.y);
  }

  point_t operator - (const point_t &b) const {
    return point_t(x - b.x, y - b.y);
  }

  long long operator * (const point_t &b) const {
    return (long long)x * b.y - (long long)y * b.x;
  }
};

vector<point_t> convex_hull(vector<point_t> points) {
  sort(points.begin(), points.end());
  vector<point_t> hull;
  for (int iter = 0; iter < 2; ++iter) {
    int limit = hull.size();
    for (auto p : points) {
      while (hull.size() > limit + 1 && (hull[hull.size() - 1] - hull[hull.size() - 2]) * (p - hull[hull.size() - 2]) <= 0) {
        hull.pop_back();
      }
      hull.push_back(p);
    }
    hull.pop_back();
    reverse(points.begin(), points.end());
  }
  return hull;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  vector<pair<int, int>> answer;
  vector<point_t> p(n);
  bool same = true;
  for (int i = 0; i < n; ++i) {
    scanf("%d %d %d", &p[i].x, &p[i].y, &p[i].color);
    p[i].id = i;
    if (p[i].color != p[0].color) {
      same = false;
    }
  }
  if (same) {
    printf("%d\n", n - 1);
    for (int i = 1; i < n; ++i) {
      printf("%d %d\n", 0, i);
    }
    return 0;
  }

  vector<point_t> hull = convex_hull(p);
  int diff = 0;
  for (int i = 0; i < hull.size(); ++i) {
    if (hull[i].color != hull[(i + 1) % hull.size()].color) {
      ++diff;
    }
  }
  if (diff > 2) {
    puts("Impossible");
    return 0;
  }

  auto in_triangle = [&](point_t p, point_t a, point_t b, point_t c) {
    if (p.id == a.id || p.id == b.id || p.id == c.id) {
      return false;
    }
    int ab = (a - p) * (b - p) > 0, bc = (b - p) * (c - p) > 0, ca = (c - p) * (a - p) > 0;
    return ab == bc && bc == ca;
  };

  function<void(vector<point_t>, point_t, point_t, point_t)> solve = [&](vector<point_t> all, point_t a, point_t b, point_t c) {
    if (all.empty()) {
      return;
    }
    int p = -1;
    for (int i = 0; i < all.size(); ++i) {
      if (all[i].color == a.color) {
        p = i;
        break;
      }
    }
    if (~p) {
      point_t d = all[p];
      answer.emplace_back(a.id, d.id);
      vector<point_t> ab, bc, ca;
      for (auto p : all) {
        if (in_triangle(p, a, b, d)) {
          ab.push_back(p);
        }
        if (in_triangle(p, b, c, d)) {
          bc.push_back(p);
        }
        if (in_triangle(p, c, a, d)) {
          ca.push_back(p);
        }
      }
      solve(ab, b, a, d);
      solve(bc, d, b, c);
      solve(ca, c, a, d);
    } else {
      for (auto p : all) {
        answer.emplace_back(p.id, c.id);
      }
    }
  };
  
  if (!diff) {
    for (int i = 1; i < hull.size(); ++i) {
      answer.emplace_back(hull[i - 1].id, hull[i].id);
    }
    int middle = -1;
    for (int i = 0; i < n; ++i) {
      if (p[i].color != hull[0].color) {
        middle = i;
        break;
      }
    }
    for (int i = 0; i < hull.size(); ++i) {
      vector<point_t> all;
      for (int j = 0; j < n; ++j) {
        if (in_triangle(p[j], hull[i], hull[(i + 1) % hull.size()], p[middle])) {
          all.push_back(p[j]);
        }
      }
      solve(all, p[middle], hull[i], hull[(i + 1) % hull.size()]);
    }
  } else {
    int first = 0;
    while (first < hull.size() && hull[first].color == hull[0].color) {
      ++first;
    }
    rotate(hull.begin(), hull.begin() + first, hull.end());
    first = 0;
    while (first < hull.size() && hull[first].color == hull[0].color) {
      ++first;
    }
    for (int i = 1; i < first; ++i) {
      answer.emplace_back(hull[i - 1].id, hull[i].id);
      vector<point_t> all;
      for (int j = 0; j < n; ++j) {
        if (in_triangle(p[j], hull[first], hull[i], hull[i - 1])) {
          all.push_back(p[j]);
        }
      }
      solve(all, hull[first], hull[i], hull[i - 1]);
    }
    for (int i = first + 1; i < hull.size(); ++i) {
      answer.emplace_back(hull[i - 1].id, hull[i].id);
      vector<point_t> all;
      for (int j = 0; j < n; ++j) {
        if (in_triangle(p[j], hull[0], hull[i], hull[i - 1])) {
          all.push_back(p[j]);
        }
      }
      solve(all, hull[0], hull[i], hull[i - 1]);
    }
  }

  printf("%d\n", answer.size());
  for (auto p : answer) {
    printf("%d %d\n", p.first, p.second);
  }
  return 0;
}

Shady Lady

一个结论是:将 x a y b x^ay^b xayb 当成平面上的点 ( a , b ) (a, b) (a,b) ,一个式子有界当且仅当这个式子的所有点加上原点构成的凸包上的点坐标都是偶数。

对于一个这样的凸包 P P P ,可以给出构造:凸包上的点系数取 n n n ,其他点系数取 1 1 1 ,则凸包内部的点 Q = ( a , b ) Q = (a, b) Q=(a,b) 假设可以表示成 ∑ α i P i ( ∑ α i = 1 ) \sum \alpha_i P_i(\sum \alpha_i = 1) αiPi(αi=1) ,则根据均值不等式可以推出 ∑ α i x x i y y i ≥ ∣ x a y b ∣ \sum \alpha_i x^{x_i} y^{y_i}\ge |x^ay^b| αixxiyyixayb

否则,考虑一个横坐标(纵坐标同理)为奇数的点 Q = ( a , b ) Q = (a, b) Q=(a,b) ,一定有一条直线可以截这个点,假设原点到这条直线的向量是 ( p , q ) (p, q) (p,q) ,用 ( − ∞ p , ∞ q ) (-\infty^p, \infty^q) (p,q) 就可以让它没有下界。

如果原凸包上有解则一定有解,否则一定是去掉 P i P_i Pi 之后在 P i − 1 , P i + 1 P_{i-1}, P_{i+1} Pi1,Pi+1 之间的新点构成凸包存在奇数坐标的点,注意到这些过程可以一起做:将凸包黑白染色,删去黑点和白点分别再做一次凸包检验即可。

#include <bits/stdc++.h>

using namespace std;

struct point_t {
  int x, y, id;

  point_t() {
  }

  point_t(int x, int y):x(x), y(y) {
  }

  point_t(int x, int y, int id):x(x), y(y), id(id) {
  }

  bool operator < (const point_t &b) const {
    return x < b.x || (x == b.x && y < b.y);
  }

  point_t operator - (const point_t &b) const {
    return point_t(x - b.x, y - b.y);
  }

  long long operator * (const point_t &b) const {
    return (long long)x * b.y - (long long)y * b.x;
  }
};

vector<point_t> convex_hull(vector<point_t> points) {
  sort(points.begin(), points.end());
  vector<point_t> hull;
  for (int iter = 0; iter < 2; ++iter) {
    int limit = hull.size();
    for (auto p : points) {
      while (hull.size() > limit + 1 && (hull[hull.size() - 1] - hull[hull.size() - 2]) * (p - hull[hull.size() - 2]) <= 0) {
        hull.pop_back();
      }
      hull.push_back(p);
    }
    hull.pop_back();
    reverse(points.begin(), points.end());
  }
  return hull;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  vector<point_t> p(n);
  bool origin = false;
  for (int i = 0; i < n; ++i) {
    scanf("%d %d", &p[i].x, &p[i].y);
    if (!p[i].x && !p[i].y) {
      origin = true;
    }
    p[i].id = i;
  }
  if (!origin) {
    p.emplace_back(0, 0, n++);
  }
  vector<point_t> hull = convex_hull(p);
  for (auto p : hull) {
    if ((p.x & 1) || (p.y & 1)) {
      puts("Ani");
      return 0;
    }
  }
  {
    vector<bool> ban(n);
    for (int i = 1; i < hull.size(); i += 2) {
      ban[hull[i].id] = true;
    }
    vector<point_t> q;
    for (int i = 0; i < n; ++i) {
      if (!ban[i]) {
        q.push_back(p[i]);
      }
    }
    q = convex_hull(q);
    for (auto p : q) {
      if ((p.x & 1) || (p.y & 1)) {
        puts("Ani");
        return 0;
      }
    }
  }
  {
    vector<bool> ban(n);
    for (int i = 2; i < hull.size(); i += 2) {
      ban[hull[i].id] = true;
    }
    vector<point_t> q;
    for (int i = 0; i < n; ++i) {
      if (!ban[i]) {
        q.push_back(p[i]);
      }
    }
    q = convex_hull(q);
    for (auto p : q) {
      if ((p.x & 1) || (p.y & 1)) {
        puts("Ani");
        return 0;
      }
    }
  }
  puts("Borna");
  return 0;
}

AI robots

按照 r r r 从大到小排序,那么只需要考虑当前的限制,暴力枚举 q q q 那一维,坐标那一维用个数据结构维护。

#include <bits/stdc++.h>

using namespace std;

struct fenwick_t {
  vector<int> all, fenw;
  int n;

  void init() {
    sort(all.begin(), all.end());
    all.erase(unique(all.begin(), all.end()), all.end());
    n = all.size();
    fenw.resize(n);
  }

  void modify(int x) {
    x = lower_bound(all.begin(), all.end(), x) - all.begin();
    while (x < n) {
      ++fenw[x];
      x |= x + 1;
    }
  }

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

  int query(int l, int r) {
    return query(upper_bound(all.begin(), all.end(), r) - all.begin() - 1) - query(lower_bound(all.begin(), all.end(), l) - all.begin() - 1);
  }
};

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n, m;
  scanf("%d %d", &n, &m);
  vector<int> x(n), r(n), q(n), p(n), disc(n);
  for (int i = 0; i < n; ++i) {
    scanf("%d %d %d", &x[i], &r[i], &q[i]);
    disc[i] = q[i];
    p[i] = i;
  }
  sort(disc.begin(), disc.end());
  disc.erase(unique(disc.begin(), disc.end()), disc.end());
  vector<fenwick_t> fenwick(disc.size());
  
  auto id = [&](int x) {
    int p = lower_bound(disc.begin(), disc.end(), x) - disc.begin();
    if (p < disc.size() && disc[p] == x) {
      return p;
    } else {
      return -1;
    }
  };

  for (int i = 0; i < n; ++i) {
    fenwick[id(q[i])].all.push_back(x[i]);
  }
  for (int i = 0; i < disc.size(); ++i) {
    fenwick[i].init();
  }

  sort(p.begin(), p.end(), [&](const int &x, const int &y) {
    return r[x] > r[y];
  });
  long long answer = 0;
  for (auto i : p) {
    for (int j = q[i] - m; j <= q[i] + m; ++j) {
      int p = id(j);
      if (~p) {
        answer += fenwick[p].query(x[i] - r[i], x[i] + r[i]);
      }
    }
    fenwick[id(q[i])].modify(x[i]);
  }
  printf("%lld\n", answer);
  return 0;
}

Self-exploration

将最后的数中 00 00 00 11 11 11 缩起来,不难发现 0 ≤ c 10 − c 01 ≤ 1 0\le c_{10} - c_{01}\le 1 0c10c011 。所以可以快速算出有多少个 0 0 0 和多少个 1 1 1 已经 0 0 0 1 1 1 有多少段。如果没有上界限制答案就是两个组合数乘起来,有上界枚举哪一维脱离上界计算即可。

#include <bits/stdc++.h>

using namespace std;

const int md = 1e9 + 7;

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

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

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

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  string low, high;
  cin >> low >> high;
  for (int i = high.length() - 1; ~i; --i) {
    if (high[i] == '0') {
      high[i] = '1';
      break;
    } else {
      high[i] = '0';
      if (!i) {
        high = "1" + high;
      }
    }
  }
  int zz, zo, oz, oo;
  cin >> zz >> zo >> oz >> oo;
  if (oz - zo < 0 || oz - zo > 1) {
    puts("0");
    return 0;
  }
  
  int one = oz + (oz == zo), zero = zo + (oz > zo), ones = one + oo, zeros = zero + zz, n = ones + zeros;
  vector<int> fac(n + 1), ifac(n + 1);
  fac[0] = fac[1] = ifac[0] = ifac[1] = 1;
  for (int i = 2; i <= n; ++i) {
    fac[i] = mul(fac[i - 1], i);
    ifac[i] = mul(md - md / i, ifac[md % i]);
  }
  for (int i = 2; i <= n; ++i) {
    ifac[i] = mul(ifac[i], ifac[i - 1]);
  }

  auto binom = [&](int x, int y) {
    if (x < 0 || y < 0 || x < y) {
      return 0;
    } else {
      return mul(fac[x], mul(ifac[y], ifac[x - y]));
    }
  };

  auto solve = [&](string limit) {
    if (limit.length() < n) {
      return 0;
    } else if (limit.length() > n) {
      return mul(binom(ones - 1, one - 1), !zeros && !zero ? 1 : binom(zeros - 1, zero - 1));
    } else {
      int o = 0, z = 0, os = 0, zs = 0, last = 0, answer = 0;
      for (int i = 0; i < limit.size(); ++i) {
        if (i && limit[i] == '1') {
          ++zs;
          if (last) {
            ++z;
          }
          answer = add(answer, mul(ones == os && one == o ? 1 : binom(ones - os - 1, one - o - 1), binom(zeros - zs, zero - z)));
          --zs;
          if (last) {
            --z;
          }
        }
        if (limit[i] == '1') {
          ++os;
          if (!last) {
            last = 1;
            ++o;
          }
        } else {
          ++zs;
          if (last) {
            last = 0;
            ++z;
          }
        }
      }
      return answer;
    }
  };

  printf("%d\n", sub(solve(high), solve(low)));
  return 0;
}

Palindrome Pairs

暴力。

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  cin >> n;
  map<int, int> a;
  for (int i = 0; i < n; ++i) {
    string s;
    cin >> s;
    int state = 0;
    for (auto c : s) {
      state ^= 1 << c - 'a';
    }
    ++a[state];
  }
  long long answer = 0;
  for (auto p : a) {
    if (p.second) {
      answer += (long long)p.second * (p.second - 1);
      for (int i = 0; i < 26; ++i) {
        answer += (long long)p.second * a[p.first ^ 1 << i];
      }
    }
  }
  printf("%lld\n", answer >> 1);
  return 0;
}

Moonwalk challenge

原题链接

暴力提取 LCA 附近的一段,其他部分一定是返祖链,维护哈希即可。

#include <bits/stdc++.h>

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 + 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);
  }

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

vector<int> kmp(vector<int> a) {
  int n = a.size();
  vector<int> fail(n);
  fail[0] = -1;
  for (int i = 1; i < n; ++i) {
    fail[i] = fail[i - 1];
    while (~fail[i] && a[fail[i] + 1] != a[i]) {
      fail[i] = fail[fail[i]];
    }
    if (a[fail[i] + 1] == a[i]) {
      ++fail[i];
    }
  }
  return fail;
}

int match(vector<int> a, vector<int> b) {
  vector<int> fail = kmp(b);
  int n = a.size(), m = b.size(), result = 0;
  for (int i = 0, j = -1; i < n; ++i) {
    while (~j && a[i] != b[j + 1]) {
      j = fail[j];
    }
    if (a[i] == b[j + 1]) {
      ++j;
    }
    if (j == m - 1) {
      ++result;
      j = fail[j];
    }
  }
  return result;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  const int m = 100;

  ios::sync_with_stdio(0);
  cin.tie(0);

  int n;
  cin >> n;
  vector<vector<pair<int, int>>> adj(n);
  for (int i = 0; i < n - 1; ++i) {
    int x, y;
    string s;
    cin >> x >> y >> s;
    --x;
    --y;
    adj[x].emplace_back(y, s[0] - 'a' + 1);
    adj[y].emplace_back(x, s[0] - 'a' + 1);
  }

  int log_n = 0;
  while (1 << log_n < n) {
    ++log_n;
  }
  vector<vector<int>> jump(m + 1, vector<int> (n, -1)), parent(log_n, vector<int> (n, -1));
  vector<hash_t> hash(n), power(n);
  vector<int> depth(n), value(n);
  power[0] = hash_t(1, 1);
  for (int i = 1; i < n; ++i) {
    power[i] = power[i - 1] * base;
  }
  
  function<void(int)> dfs = [&](int x) {
    for (int i = 1; depth[x] >> i; ++i) {
      parent[i][x] = parent[i - 1][parent[i - 1][x]];
    }
    jump[0][x] = x;
    for (int i = 1; i <= m && i <= depth[x]; ++i) {
      jump[i][x] = jump[i - 1][parent[0][x]];
    }
    for (auto e : adj[x]) {
      int y = e.first, w = e.second;
      if (y != parent[0][x]) {
        value[y] = w;
        parent[0][y] = x;
        depth[y] = depth[x] + 1;
        hash[y] = hash[x] * base + w;
        dfs(y);
      }
    }
  };

  auto go_up = [&](int x, int d) {
    for (int i = 0; depth[x] > d; ++i) {
      if (depth[x] - d >> i & 1) {
        x = parent[i][x];
      }
    }
    return x;
  };

  auto lca = [&](int x, int y) {
    if (depth[x] < depth[y]) {
      swap(x, y);
    }
    x = go_up(x, depth[y]);
    if (x == y) {
      return x;
    }
    for (int i = log_n - 1; ~i; --i) {
      if (parent[i][x] != parent[i][y]) {
        x = parent[i][x];
        y = parent[i][y];
      }
    }
    return parent[0][x];
  };

  dfs(0);
  int q;
  cin >> q;
  vector<int> answer(q);
  vector<vector<pair<int, pair<long long, int>>>> event(m + 1);

  for (int i = 0; i < q; ++i) {
    int x, y;
    string s;
    cin >> x >> y >> s;
    --x;
    --y;
    if (x == y) {
      continue;
    }

    int l = s.length(), z = lca(x, y);
    int u = go_up(x, min(depth[x], depth[z] + l - 1));
    int v = go_up(y, min(depth[y], depth[z] + l - 1));
    vector<int> left, right;
    for (int t = u; t != z; t = parent[0][t]) {
      left.push_back(value[t]);
    }
    for (int t = v; t != z; t = parent[0][t]) {
      right.push_back(value[t]);
    }
    reverse(right.begin(), right.end());
    for (auto t : right) {
      left.push_back(t);
    }
    right.clear();
    for (auto c : s) {
      right.push_back(c - 'a' + 1);
    }
    answer[i] = match(left, right);
    
    if (x != u) {
      hash_t hash(0, 0);
      for (int t = right.size() - 1; ~t; --t) {
        hash = hash * base + right[t];
      }
      event[l].emplace_back(x, make_pair(hash.get(), i));
      event[l].emplace_back(u, make_pair(hash.get(), i + q));
    }
    if (y != v) {
      hash_t hash(0, 0);
      for (int t = 0; t < right.size(); ++t) {
        hash = hash * base + right[t];
      }
      event[l].emplace_back(y, make_pair(hash.get(), i));
      event[l].emplace_back(v, make_pair(hash.get(), i + q));
    }
  }

  auto get_hash = [&](int x, int y) {
    return (hash[x] - hash[y] * power[depth[x] - depth[y]]).get();
  };

  for (int i = 1; i <= m; ++i) {
    if (!event[i].empty()) {
      vector<vector<pair<long long, int>>> queries(n);
      for (auto e : event[i]) {
        queries[e.first].push_back(e.second);
      }
      unordered_map<long long, int> appear;

      function<void(int)> solve = [&](int x) {
        if (~jump[i][x]) {
          ++appear[get_hash(x, jump[i][x])];
        }
        for (auto e : queries[x]) {
          if (e.second < q) {
            answer[e.second] += appear[e.first];
          } else {
            answer[e.second - q] -= appear[e.first];
          }
        }
        for (auto e : adj[x]) {
          if (e.first != parent[0][x]) {
            solve(e.first);
          }
        }
        if (~jump[i][x]) {
          --appear[get_hash(x, jump[i][x])];
        }
      };

      solve(0);
    }
  }

  for (int i = 0; i < q; ++i) {
    printf("%d\n", answer[i]);
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值