CodeForces Gym 101955 简要题解

Sockpuppets

建出trie树,那么匹配的东西一定是祖先关系。

f ( x , a , b ) f(x,a,b) f(x,a,b) 表示考虑了 x x x 的子树,祖先有 a a a 个小号匹配子树,子树有 b b b 个小号匹配祖先的方案数,背包转移即可。

#include 

using namespace std;

const int N = 23456;
const int ALPHA = 26;
const int md = 1e9 + 7;

int n, m, root, total, type[N], go[N][ALPHA], dp[N][11][21];
char str[N];

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

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

inline int new_node() {
  ++total;
  for (int i = 0; i < ALPHA; ++i) {
    go[total][i] = 0;
  }
  type[total] = 0;
  return total;
}

void dfs(int x) {
  static int temp[11][21];
  memset(dp[x], 0, sizeof dp[x]);
  dp[x][0][0] = 1;
  for (int i = 0; i < ALPHA; ++i) {
    int y = go[x][i];
    if (y) {
      dfs(y);
    }
  }
  for (int i = 0; i < ALPHA; ++i) {
    int y = go[x][i];
    if (y) {
      for (int a = 0; a <= 10; ++a) {
        for (int b = 0; b <= 20; ++b) {
          if (!dp[x][a][b]) {
            continue;
          }
          for (int aa = 0; aa <= 10 - a; ++aa) {
            for (int bb = 0; bb <= 20 - b; ++bb) {
              if (dp[y][aa][bb]) {
                add(temp[a + aa][b + bb], mul(dp[x][a][b], dp[y][aa][bb]));
              }
            }
          }
        }
      }
      for (int a = 0; a <= 10; ++a) {
        for (int b = 0; b <= 20; ++b) {
          dp[x][a][b] = temp[a][b];
          temp[a][b] = 0;
        }
      }
    }
  }
  if (type[x] == 1) {
    for (int a = 0; a <= 10; ++a) {
      for (int b = 0; b <= 20; ++b) {
        if (!dp[x][a][b]) {
          continue;
        }
        add(temp[a][b], dp[x][a][b]);
        if (a + 1 <= 10) {
          add(temp[a + 1][b], dp[x][a][b]);
        }
        if (b - 1 >= 0) {
          add(temp[a][b - 1], mul(b, dp[x][a][b]));
        }
        if (a + 2 <= 10) {
          add(temp[a + 2][b], mul(md + 1 >> 1, dp[x][a][b]));
        }
        if (a + 1 <= 10 && b - 1 >= 0) {
          add(temp[a + 1][b - 1], mul(b, dp[x][a][b]));
        }
        if (b - 2 >= 0) {
          add(temp[a][b - 2], mul(b * (b - 1) / 2, dp[x][a][b]));
        }
      }
    }
    for (int a = 0; a <= 10; ++a) {
      for (int b = 0; b <= 20; ++b) {
        dp[x][a][b] = temp[a][b];
        temp[a][b] = 0;
      }
    }
  } else if (type[x] == 2) {
    for (int a = 0; a <= 10; ++a) {
      for (int b = 0; b <= 20; ++b) {
        if (!dp[x][a][b]) {
          continue;
        }
        add(temp[a][b], dp[x][a][b]);
        if (a - 1 >= 0) {
          add(temp[a - 1][b], mul(a, dp[x][a][b]));
        }
        if (b + 1 <= 20) {
          add(temp[a][b + 1], dp[x][a][b]);
        }
      }
    }
    for (int a = 0; a <= 10; ++a) {
      for (int b = 0; b <= 20; ++b) {
        dp[x][a][b] = temp[a][b];
        temp[a][b] = 0;
      }
    }
  }
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int tt;
  scanf("%d", &tt);
  for (int ttt = 1; ttt <= tt; ++ttt) {
    scanf("%d %d", &n, &m);
    total = 0;
    root = new_node();
    for (int i = 1; i <= n; ++i) {
      scanf("%s", str);
      int x = root;
      for (int j = 0; str[j]; ++j) {
        if (!go[x][str[j] - 'a']) {
          go[x][str[j] - 'a'] = new_node();
        }
        x = go[x][str[j] - 'a'];
      }
      type[x] = 1;
    }
    for (int i = 1; i <= m; ++i) {
      scanf("%s", str);
      int x = root;
      for (int j = 0; str[j]; ++j) {
        if (!go[x][str[j] - 'a']) {
          go[x][str[j] - 'a'] = new_node();
        }
        x = go[x][str[j] - 'a'];
      }
      type[x] = 2;
    }
    dfs(root);
    printf("Case #%d: %d\n", ttt, dp[root][0][0]);
  }
  return 0;
}

Sequences Generator

先默认每个位置是最大概率的数,每次失配会让概率乘最多 1 2 \frac{1}{2} 21 ,用后缀数组暴力匹配,当概率小于 e p s eps eps 时直接返回 0 0 0 即可。

#include 

using namespace std;

typedef double ld;

const int N = 623456;
const int LOG = 20;
const ld eps = 1e-12;

int n, m, low[N], high[N], prob[N][10];
ld sum[N];

struct suffix_array_t {
  int n, s[N], rank[N], array[N], height[N], length[N], rmq[LOG][N];

  void init() {
    static int temp[N], first[N], second[N], number[N];
    for (int i = 1; i <= n; ++i) {
      ++number[s[i]];
    }
    for (int i = 1; i <= n; ++i) {
      number[i] += number[i - 1];
    }
    for (int i = 1; i <= n; ++i) {
      array[number[s[i]]--] = i;
    }
    for (int i = 1; i <= n; ++i) {
      number[i] = 0;
    }
    rank[array[1]] = 1;
    for (int i = 2; i <= n; ++i) {
      rank[array[i]] = rank[array[i - 1]] + (s[array[i]] != s[array[i - 1]]);
    }
    for (int length = 1; rank[array[n]] != n; length <<= 1) {
      for (int i = 1; i <= n; ++i) {
        ++number[second[i] = i + length > n ? 0 : rank[i + length]];
      }
      for (int i = 1; i <= n; ++i) {
        number[i] += number[i - 1];
      }
      for (int i = 1; i <= n; ++i) {
        temp[number[second[i]]--] = i;
      }
      for (int i = 0; i <= n; ++i) {
        number[i] = 0;
      }
      for (int i = 1; i <= n; ++i) {
        ++number[first[i] = rank[i]];
      }
      for (int i = 1; i <= n; ++i) {
        number[i] += number[i - 1];
      }
      for (int i = n; i; --i) {
        array[number[first[temp[i]]]--] = temp[i];
      }
      for (int i = 1; i <= n; ++i) {
        number[i] = 0;
      }
      rank[array[1]] = 1;
      for (int i = 2; i <= n; ++i) {
        rank[array[i]] = rank[array[i - 1]] + (first[array[i]] != first[array[i - 1]] || second[array[i]] != second[array[i - 1]]);
      }
    }
    for (int i = 1, j = 0; i <= n; ++i) {
      for (j -= j > 0; rank[i] != 1 && i + j <= n && array[rank[i] - 1] + j <= n && s[i + j] == s[array[rank[i] - 1] + j]; ++j);
      height[rank[i]] = j;
    }
    for (int i = 2; i <= n; ++i) {
      length[i] = length[i >> 1] + 1;
    }
    for (int i = 1; i <= n; ++i) {
      rmq[0][i] = height[i];
    }
    for (int i = 1; i < LOG; ++i) {
      for (int j = 1; j + (1 << i) - 1 <= n; ++j) {
        rmq[i][j] = min(rmq[i - 1][j], rmq[i - 1][j + (1 << i - 1)]);
      }
    }
  }

  int lcp(int x, int y) {
    if (x == y) {
      return n - x + 1;
    }
    x = rank[x];
    y = rank[y];
    if (x > y) {
      swap(x, y);
    }
    ++x;
    int k = length[y - x];
    return min(rmq[k][x], rmq[k][y - (1 << k) + 1]);
  }
} suffix_array;

ld solve(int l, int r, int ll, int rr, ld value) {
  if (value < eps) {
    return 0;
  }
  if (l > r) {
    return value;
  }
  int lcp = suffix_array.lcp(l, ll);
  if (lcp >= r - l + 1) {
    return value * exp(sum[r] - sum[l - 1]);
  }
  value *= exp(sum[l + lcp - 1] - sum[l - 1]);
  l += lcp;
  ll += lcp;
  if (low[l] <= suffix_array.s[ll] && high[l] >= suffix_array.s[ll]) {
    value *= prob[l][suffix_array.s[ll] - low[l]] * 1e-9;
    return solve(l + 1, r, ll + 1, rr, value);
  } else {
    return 0;
  }
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int tt;
  scanf("%d", &tt);
  for (int ttt = 1; ttt <= tt; ++ttt) {
    printf("Case #%d:\n", ttt);
    scanf("%d %d", &n, &m);
    suffix_array.n = n + m;
    for (int i = 1; i <= n; ++i) {
      scanf("%d %d", &low[i], &high[i]);
      int p = 0;
      for (int j = low[i]; j <= high[i]; ++j) {
        scanf("%d", &prob[i][j - low[i]]);
        if (prob[i][j - low[i]] > prob[i][p]) {
          p = j - low[i];
        }
      }
      suffix_array.s[i] = p + low[i];
    }
    for (int i = 1; i <= m; ++i) {
      scanf("%d", &suffix_array.s[i + n]);
    }
    suffix_array.init();
    for (int i = 1; i <= n; ++i) {
      sum[i] = sum[i - 1] + log(prob[i][suffix_array.s[i] - low[i]] * 1e-9);
    }
    for (int i = 1; i <= n - m + 1; ++i) {
      printf("%.12lf\n", solve(i, i + m - 1, n + 1, n + m, 1));
    }
  }
  return 0;
}

Insertion Sort

相当于 min ⁡ ( n , k ) \min(n,k) min(n,k) 个位置是有序的,枚举最后的序列,这样的序列只有 O ( n 2 ) O(n^2) O(n2) 个,可以快速判断每个是否合法,最后答案乘上 k ! k! k! 即可。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int tt;
  scanf("%d", &tt);
  for (int ttt = 1; ttt <= tt; ++ttt) {
    int n, m, md;
    scanf("%d %d %d", &n, &m, &md);
    m = min(m, n);
    int answer = 1;
    for (int i = 1; i <= n; ++i) {
      for (int j = 1; j <= n; ++j) {
        if (j != i) {
          if (j > m || (j == m && i > m)) {
            ++answer;
          }
        }
      }
    }
    for (int i = m; i < n; ++i) {
      --answer;
    }
    for (int i = 1; i <= m; ++i) {
      answer = (long long)answer * i % md;
    }
    printf("Case #%d: %d\n", ttt, answer);
  }
  return 0;
}

Diameter of a Tree

边分治,闵可夫斯基和。

#include 

using namespace std;

typedef long long ll;

const int N = 456789;

struct point_t {
  ll x, y;

  point_t(ll x = 0, ll y = 0):x(x), y(y) {
  }

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

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

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

  ll solve(int k) {
    return k * x + y;
  }
} dist[N];

vector<pair<int, point_t>> adj[N], edge[N];
int n, m, root, proot, size[N];
vector<point_t> l, r, all;
bool visit[N];

void rebuild(int x, int parent) {
  vector<pair<int, point_t>> son;
  for (auto e : edge[x]) {
    if (e.first != parent) {
      rebuild(e.first, x);
      son.push_back(e);
    }
  }
  for (int i = 0, t = x; i < son.size(); ++i) {
    if (i && i + 1 < son.size()) {
      adj[n].emplace_back(t, point_t(0, 0));
      adj[t].emplace_back(n, point_t(0, 0));
      t = n++;
    }
    adj[t].emplace_back(son[i].first, son[i].second);
    adj[son[i].first].emplace_back(t, son[i].second);
  }
}

void find_root(int x, int p, int s) {
  size[x] = 1;
  for (auto e : adj[x]) {
    int y = e.first;
    if (y != p && !visit[y]) {
      find_root(y, x, s);
      size[x] += size[y];
    }
  }
  if (!~root || max(size[root], s - size[root]) > max(size[x], s - size[x])) {
    root = x;
    proot = p;
  }
}

void init(int x, int p, bool subtree) {
  if (subtree) {
    r.push_back(dist[x]);
  } else {
    l.push_back(dist[x]);
  }
  for (auto e : adj[x]) {
    int y = e.first;
    if (y != p && !visit[y]) {
      dist[y] = dist[x] + e.second;
      init(y, x, y == root || subtree);
    }
  }
}

vector<point_t> build_convex(vector<point_t> v) {
  vector<point_t> convex;
  sort(v.begin(), v.end(), [&](const point_t &a, const point_t &b) {
    return a.x < b.x || (a.x == b.x && a.y > b.y);  
  });
  int m = 0;
  for (int i = 0; i < v.size(); ++i) {
    if (i && v[i].x == v[i - 1].x) {
      continue;
    }
    while (m && convex[m - 1].y <= v[i].y) {
      convex.pop_back();
      --m;
    }
    while (m > 1 && (v[i] - convex[m - 2]) * (convex[m - 1] - convex[m - 2]) <= 0) {
      convex.pop_back();
      --m;
    }
    convex.push_back(v[i]);
    m++;
  }
  return convex;
}

void minkowski_sum(vector<point_t> a, vector<point_t> b) {
  all.push_back(a[0] + b[0]);
  for (int i = 0, j = 0; i + 1 < a.size() || j + 1 < b.size(); ) {
    if (j + 1 == b.size() || (i + 1 < a.size() && (a[i + 1] - a[i]) * (b[j + 1] - b[j]) < 0)) {
      all.push_back(a[++i] + b[j]);
    } else {
      all.push_back(a[i] + b[++j]);
    }
  }
}

void divide(int x, int s) {
  if (s == 1) {
    return;
  }
  root = -1;
  find_root(x, -1, s);
  int u = root, v = proot, size_u = size[u], size_v = s - size[u];
  dist[v] = point_t(0, 0);
  l.clear();
  r.clear();
  init(v, -1, false);
  l = build_convex(l);
  r = build_convex(r);
  minkowski_sum(l, r);
  visit[v] = true;
  divide(u, size_u);
  visit[v] = false;
  visit[u] = true;
  divide(v, size_v);
  visit[u] = false;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int tt;
  scanf("%d", &tt);
  for (int ttt = 1; ttt <= tt; ++ttt) {
    printf("Case #%d:\n", ttt);
    scanf("%d %d", &n, &m);
    for (int i = 0; i < n - 1; ++i) {
      int x, y, a = 1, b;
      scanf("%d %d %d", &x, &y, &b);
      --x;
      --y;
      edge[x].emplace_back(y, point_t(a, b));
      edge[y].emplace_back(x, point_t(a, b));
    }
    rebuild(0, -1);
    divide(0, n);
    all = build_convex(all);
    vector<pair<int, int>> queries(m);
    for (int i = 0; i < m; ++i) {
      scanf("%d", &queries[i].first);
      queries[i].second = i;
    }
    sort(queries.begin(), queries.end());
    vector<ll> answer(m);
    int j = 0;
    for (auto p : queries) {
      int i = p.first;
      while (j + 1 < all.size() && all[j + 1].solve(i) >= all[j].solve(i)) {
        ++j;
      }
      answer[p.second] = all[j].solve(i);
    }
    for (int i = 0; i < m; ++i) {
      printf("%lld\n", answer[i]);
    }
    all.clear();
    for (int i = 0; i < n; ++i) {
      adj[i].clear();
      edge[i].clear();
      visit[i] = false;
    }
  }
  return 0;
}

The Kouga Ninja Scrolls

线段树维护每个区间 x + y , x − y , − x + y , − x − y x+y,x-y,-x+y,-x-y x+y,xy,x+y,xy 的最大次大值即可。

#include 

using namespace std;

typedef long long ll;

const int N = 123456;

struct info_t {
  int fx, fy;
  ll x, y;

  info_t(ll x = 0, int fx = 0, ll y = 0, int fy = 0):x(x), fx(fx), y(y), fy(fy) {
  }
};

struct node_t {
  info_t pp, pn, np, nn;
  ll answer;
} tree[N << 1];

int n, m, c[N];
ll x[N], y[N];

void update(info_t &info, ll x, int fx) {
  if (~fx) {
    if (info.fx == fx) {
      if (info.x < x) {
        info.x = x;
      }
    } else {
      if (info.fy == -1 || info.y < x) {
        info.y = x;
        info.fy = fx;
      }
      if (info.y > info.x) {
        swap(info.x, info.y);
        swap(info.fx, info.fy);
      }
    }
  }
}

info_t operator + (info_t a, info_t b) {
  info_t result(a.x, a.fx, 0, -1);
  update(result, a.y, a.fy);
  update(result, b.x, b.fx);
  update(result, b.y, b.fy);
  return result;
}

node_t make(int p) {
  node_t result;
  result.pp = info_t(x[p] + y[p], c[p], 0, -1);
  result.pn = info_t(x[p] - y[p], c[p], 0, -1);
  result.np = info_t(-x[p] + y[p], c[p], 0, -1);
  result.nn = info_t(-x[p] - y[p], c[p], 0, -1);
  result.answer = 0;
  return result;
}

ll merge(info_t l, info_t r) {
  if (l.fx != r.fx) {
    return l.x + r.x;
  } else {
    ll result = 0;
    if (~l.fy) {
      result = max(result, l.y + r.x);
    }
    if (~r.fy) {
      result = max(result, r.y + l.x);
    }
    return result;
  }
}

node_t unite(node_t l, node_t r) {
  node_t result;
  result.answer = max(l.answer, r.answer);
  result.pp = l.pp + r.pp;
  result.pn = l.pn + r.pn;
  result.np = l.np + r.np;
  result.nn = l.nn + r.nn;
  result.answer = max(result.answer, merge(l.pp, r.nn));
  result.answer = max(result.answer, merge(l.pn, r.np));
  result.answer = max(result.answer, merge(l.np, r.pn));
  result.answer = max(result.answer, merge(l.nn, r.pp));
  return result;
}

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

void build(int x, int l, int r) {
  if (l == r) {
    tree[x] = make(l);
  } else {
    int y = l + r >> 1, z = x + (y - l + 1 << 1);
    build(x + 1, l, y);
    build(z, y + 1, r);
    pull(x, z);
  }
}

void modify(int x, int l, int r, int p) {
  if (l == r) {
    tree[x] = make(l);
  } else {
    int y = l + r >> 1, z = x + (y - l + 1 << 1);
    if (p <= y) {
      modify(x + 1, l, y, p);
    } else {
      modify(z, y + 1, r, p);
    }
    pull(x, z);
  }
}

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

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int tt;
  scanf("%d", &tt);
  for (int ttt = 1; ttt <= tt; ++ttt) {
    printf("Case #%d:\n", ttt);
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; ++i) {
      scanf("%lld %lld %d\n", &x[i], &y[i], &c[i]);
    }
    build(1, 1, n);
    while (m--) {
      int type;
      scanf("%d", &type);
      if (type == 1) {
        int p, dx, dy;
        scanf("%d %d %d", &p, &dx, &dy);
        x[p] += dx;
        y[p] += dy;
        modify(1, 1, n, p);
      } else if (type == 2) {
        int p;
        scanf("%d", &p);
        scanf("%d", &c[p]);
        modify(1, 1, n, p);
      } else {
        int l, r;
        scanf("%d %d", &l, &r);
        printf("%lld\n", query(1, 1, n, l, r).answer);
      }
    }
  }
  return 0;
}

Counting Sheep in Ami Dongsuo

暴力是DP做卷积。可以对 1 , 2 , ⋯   , 3 w 1, 2, \cdots, 3w 1,2,,3w 代入多项式,这样DP就是点乘,最后插值回来即可。

#include 

using namespace std;

const int N = 12345;
const int M = 1234;
const int md = 1e9 + 7;

int n, m, w, inv[M], answer[M], value[N], degree[N], power[M][M], dp[N][M][3];
vector<int> order, adj[N];

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

vector<int> interpolation(vector<int> u) {
  int n = u.size();
  vector<int> result(n), sum(n);
  result[0] = u[0];
  sum[0] = 1;
  for (int i = 1; i < n; ++i) {
    for (int j = n - 1; j >= i; --j) {
      sub(u[j], u[j - 1]);
      u[j] = mul(u[j], inv[i]);
    }
    for (int j = i; ~j; --j) {
      sum[j] = mul(sum[j], md + 1 - i);
      if (j) {
        add(sum[j], sum[j - 1]);
      }
      add(result[j], mul(sum[j], u[i]));
    }
  }
  return result;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int tt;
  scanf("%d", &tt);
  for (int ttt = 1; ttt <= tt; ++ttt) {
    printf("Case #%d:", ttt);
    scanf("%d %d %d", &n, &m, &w);
    for (int i = 1; i <= n; ++i) {
      adj[i].clear();
      degree[i] = 0;
      scanf("%d", &value[i]);
    }
    w *= 3;
    inv[0] = inv[1] = 1;
    for (int i = 2; i <= w; ++i) {
      inv[i] = mul(md - md / i, inv[md % i]);
    }
    for (int i = 0; i <= w; ++i) {
      power[i][0] = 1;
      for (int j = 1; j <= w; ++j) {
        power[i][j] = mul(power[i][j - 1], i);
      }
    }
    while (m--) {
      int x, y;
      scanf("%d %d", &x, &y);
      adj[x].push_back(y);
      ++degree[y];
    }
    order.clear();
    for (int i = 1; i <= n; ++i) {
      if (!degree[i]) {
        order.push_back(i);
      }
    }
    for (int i = 0; i < n; ++i) {
      int x = order[i];
      for (auto y : adj[x]) {
        if (!--degree[y]) {
          order.push_back(y);
        }
      }
    }
    for (int i = 0; i <= w; ++i) {
      answer[i] = 0;
    }
    for (int i = n - 1; ~i; --i) {
      int x = order[i];
      for (int j = 0; j <= w; ++j) {
        for (int k = 0; k < 3; ++k) {
          dp[x][j][k] = power[j][value[x] * (k + 1)];
        }
      }
      for (auto y : adj[x]) {
        for (int j = 0; j <= w; ++j) {
          for (int k = 0; k < 3; ++k) {
            add(dp[x][j][k], dp[y][j][k]);
          }
        }
      }
      for (int j = 0; j <= w; ++j) {
        int coef = mul(dp[x][j][0], mul(dp[x][j][0], dp[x][j][0]));
        sub(coef, mul(3, mul(dp[x][j][0], dp[x][j][1])));
        add(coef, mul(2, dp[x][j][2]));
        add(answer[j], coef);
      }
    }
    vector<int> result = interpolation(vector<int> (answer, answer + w + 1));
    int inv = (md + 1) / 6;
    for (int i = 1; i <= w; ++i) {
      printf(" %d", mul(inv, result[i]));
    }
    putchar(10);
  }
  return 0;
}

Best ACMer Solves the Hardest Problem

x 2 + y 2 = k x^2+y^2=k x2+y2=k 的个数不太多,预处理后暴力即可。

#include 

using namespace std;

const int N = 12345678;
const int M = 6234;

int n, m, px[N], py[N], value[M][M];
vector<pair<int, int>> all[N];

bool inside(int x, int y) {
  return x >= 1 && x <= 6000 && y >= 1 && y <= 6000 && value[x][y];
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  for (int i = 0; i <= 6000; ++i) {
    for (int j = 0; j <= 6000; ++j) {
      if (i * i + j * j <= 10000000) {
        all[i * i + j * j].emplace_back(i, j);
      } else {
        break;
      }
    }
  }
  int tt;
  scanf("%d", &tt);
  for (int ttt = 1; ttt <= tt; ++ttt) {
    printf("Case #%d:\n", ttt);
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; ++i) {
      scanf("%d %d", &px[i], &py[i]);
      scanf("%d", &value[px[i]][py[i]]);
    }
    long long answer = 0;
    while (m--) {
      int type, x, y;
      scanf("%d %d %d", &type, &x, &y);
      x = (x + answer) % 6000 + 1;
      y = (y + answer) % 6000 + 1;
      if (type == 1) {
        scanf("%d", &value[x][y]);
        ++n;
        px[n] = x;
        py[n] = y;
      } else if (type == 2) {
        value[x][y] = 0;
      } else if (type == 3) {
        int dist, weight;
        scanf("%d %d", &dist, &weight);
        for (auto p : all[dist]) {
          if (!p.first && !p.second) {
            if (inside(x + p.first, y + p.second)) {
              value[x + p.first][y + p.second] += weight;
            }
          } else if (!p.first) {
            if (inside(x + p.first, y + p.second)) {
              value[x + p.first][y + p.second] += weight;
            }
            if (inside(x + p.first, y - p.second)) {
              value[x + p.first][y - p.second] += weight;
            }
          } else if (!p.second) {
            if (inside(x + p.first, y + p.second)) {
              value[x + p.first][y + p.second] += weight;
            }
            if (inside(x - p.first, y + p.second)) {
              value[x - p.first][y + p.second] += weight;
            }
          } else {
            if (inside(x + p.first, y + p.second)) {
              value[x + p.first][y + p.second] += weight;
            }
            if (inside(x + p.first, y - p.second)) {
              value[x + p.first][y - p.second] += weight;
            }
            if (inside(x - p.first, y + p.second)) {
              value[x - p.first][y + p.second] += weight;
            }
            if (inside(x - p.first, y - p.second)) {
              value[x - p.first][y - p.second] += weight;
            }
          }
        }
      } else {
        int dist;
        scanf("%d", &dist);
        answer = 0;
        for (auto p : all[dist]) {
          if (!p.first && !p.second) {
            if (inside(x + p.first, y + p.second)) {
              answer += value[x + p.first][y + p.second];
            }
          } else if (!p.first) {
            if (inside(x + p.first, y + p.second)) {
              answer += value[x + p.first][y + p.second];
            }
            if (inside(x + p.first, y - p.second)) {
              answer += value[x + p.first][y - p.second];
            }
          } else if (!p.second) {
            if (inside(x + p.first, y + p.second)) {
              answer += value[x + p.first][y + p.second];
            }
            if (inside(x - p.first, y + p.second)) {
              answer += value[x - p.first][y + p.second];
            }
          } else {
            if (inside(x + p.first, y + p.second)) {
              answer += value[x + p.first][y + p.second];
            }
            if (inside(x + p.first, y - p.second)) {
              answer += value[x + p.first][y - p.second];
            }
            if (inside(x - p.first, y + p.second)) {
              answer += value[x - p.first][y + p.second];
            }
            if (inside(x - p.first, y - p.second)) {
              answer += value[x - p.first][y - p.second];
            }
          }
        }
        printf("%lld\n", answer);
      }
    }
    for (int i = 1; i <= n; ++i) {
      value[px[i]][py[i]] = 0;
    }
  }
  return 0;
}

Rainbow Graph

合法的图是这种形式的: ( 1 , 2 ) , ( 3 , 4 ) ⋯   , ( 2 n − 1 , 2 n ) (1,2), (3,4)\cdots, (2n-1,2n) (1,2),(3,4),(2n1,2n) ,每对点对的连边之间要么 ( 2 i , 2 j ) ( 2 i + 1 , 2 j + 1 ) (2i,2j)(2i+1,2j+1) (2i,2j)(2i+1,2j+1) 要么 ( 2 i , 2 j + 1 ) ( 2 i + 1 , 2 j ) (2i,2j+1)(2i+1,2j) (2i,2j+1)(2i+1,2j) 。相当于 n n n 个点的完全图,把边黑白染色,可以将一个点的边全部取反。

oeis

用burnside统计答案,显然只用枚举每个环长,对于一组环长,答案是 2 E − V + o d d 2^{E-V+odd} 2EV+odd E E E 是边等价类个数, V V V 是点等价类个数, o d d odd odd 是是否存在奇环。

#include 

using namespace std;

const int N = 67;

int n, md, answer, a[N], inv[N], binary[N * N], gcd[N][N];

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

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

void dfs(int x, int last, int remain, int same, int coef, int rank, int odd) {
  if (!remain) {
    add(answer, mul(coef, binary[rank + odd]));
  } else {
    for (a[x] = last; a[x] <= remain; ++a[x]) {
      int new_coef = mul(coef, mul(inv[a[x]], a[x] == last ? inv[same + 1] : 1));
      int new_rank = rank + (a[x] >> 1) - 1;
      for (int i = 0; i < x; ++i) {
        new_rank += gcd[a[x]][a[i]];
      }
      dfs(x + 1, a[x], remain - a[x], a[x] == last ? same + 1 : 1, new_coef, new_rank, odd | (a[x] & 1));
    }
  }
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int tt;
  scanf("%d", &tt);
  for (int ttt = 1; ttt <= tt; ++ttt) {
    scanf("%d %d", &n, &md);
    for (int i = 1; i <= n; ++i) {
      for (int j = 1; j <= n; ++j) {
        gcd[i][j] = __gcd(i, j);
      }
    }
    inv[0] = inv[1] = 1;
    for (int i = 2; i <= n; ++i) {
      inv[i] = mul(md - md / i, inv[md % i]);
    }
    binary[0] = 1;
    for (int i = 1; i <= n * n; ++i) {
      binary[i] = mul(binary[i - 1], 2);
    }
    answer = 0;
    dfs(0, 1, n, 0, 1, 0, 0);
    printf("Case #%d: %d\n", ttt, answer);
  }
  return 0;
}

Distance Between Sweethearts

枚举 max ⁡ ( ∣ a i − a j ∣ , ∣ b i − b j ∣ , ∣ c i − c j ∣ ) \max(\lvert a_i-a_j\rvert, \lvert b_i-b_j\rvert, \lvert c_i-c_j\rvert) max(aiaj,bibj,cicj) ,然后插入,按位统计答案即可。

#include 

using namespace std;

typedef unsigned long long ull;

const int N = 2345;

int a[N], b[N], c[N], f[11][2], g[11][2];
vector<int> ea[N], eb[N], ec[N];
ull value[11][2];

void merge(int *a, int *b) {
  for (int i = 0; i < 11; ++i) {
    f[i][0] = f[i][1] = g[i][0] = g[i][1] = value[i][0] = value[i][1] = 0;
  }
  for (int i = 0; i < 2048; ++i) {
    if (a[i]) {
      for (int j = 0; j < 11; ++j) {
        f[j][i >> j & 1] += a[i];
      }
    }
    if (b[i]) {
      for (int j = 0; j < 11; ++j) {
        g[j][i >> j & 1] += b[i];
      }
    }
  }
  for (int i = 0; i < 11; ++i) {
    for (int j = 0; j < 2; ++j) {
      for (int k = 0; k < 2; ++k) {
        value[i][!(j ^ k)] += (1ull << i) * f[i][j] * g[i][k];
      }
    }
  }
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int tt;
  scanf("%d", &tt);
  for (int ttt = 1; ttt <= tt; ++ttt) {
    int a0, b0, c0, a1, b1, c1;
    scanf("%d %d %d %d %d %d", &a0, &b0, &c0, &a1, &b1, &c1);
    for (int i = 0; i < 2048; ++i) {
      ea[i].clear();
      eb[i].clear();
      ec[i].clear();
      a[i] = b[i] = c[i] = 0;
    }
    for (int i = 0; i <= a0; ++i) {
      for (int j = 0; j <= a1; ++j) {
        ea[abs(i - j)].push_back(i ^ j);
      }
    }
    for (int i = 0; i <= b0; ++i) {
      for (int j = 0; j <= b1; ++j) {
        eb[abs(i - j)].push_back(i ^ j);
      }
    }
    for (int i = 0; i <= c0; ++i) {
      for (int j = 0; j <= c1; ++j) {
        ec[abs(i - j)].push_back(i ^ j);
      }
    }
    ull answer = 0;
    for (int i = 0; i < 2048; ++i) {
      if (!ea[i].empty()) {
        merge(b, c);
        for (auto p : ea[i]) {
          ++a[p];
          for (int j = 0; j < 11; ++j) {
            answer += value[j][(p ^ i) >> j & 1];
          }
        }
      }
      if (!eb[i].empty()) {
        merge(a, c);
        for (auto p : eb[i]) {
          ++b[p];
          for (int j = 0; j < 11; ++j) {
            answer += value[j][(p ^ i) >> j & 1];
          }
        }
      }
      if (!ec[i].empty()) {
        merge(a, b);
        for (auto p : ec[i]) {
          ++c[p];
          for (int j = 0; j < 11; ++j) {
            answer += value[j][(p ^ i) >> j & 1];
          }
        }
      }
    }
    printf("Case #%d: %llu\n", ttt, answer);
  }
  return 0;
}

How Much Memory Your Code Is Using?

模拟。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  map<string, int> each;
  each["char"] = each["bool"] = 1;
  each["int"] = each["float"] = 4;
  each["long long"] = each["double"] = 8;
  each["__int128"] = each["long double"] = 16;
  int tt;
  cin >> tt;
  for (int ttt = 1; ttt <= tt; ++ttt) {
    int n;
    cin >> n;
    int answer = 0;
    while (n--) {
      string s;
      cin >> s;
      if (s == "long") {
        string t;
        cin >> t;
        s = s + " " + t;
      }
      int type = each[s];
      cin >> s;
      bool inside = false;
      int size = 0;
      for (auto c : s) {
        if (c == '[') {
          inside = true;
        } else if (c == ']') {
          inside = false;
        } else if (inside) {
          size = size * 10 + c - '0';
        }
      }
      if (!size) {
        size = 1;
      }
      answer += type * size;
    }
    answer = answer + 1023 >> 10;
    cout << "Case #" << ttt << ": " << answer << endl;
  }
  return 0;
}

Let the Flames Begin

考虑最后一次删除,这个人的编号是 ( k − 1 )   m o d   ( n − m + 1 ) (k-1) \bmod (n-m+1) (k1)mod(nm+1) ,然后倒着复原删除的过程即可。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int tt;
  scanf("%d", &tt);
  for (int ttt = 1; ttt <= tt; ++ttt) {
    long long n, m, k, answer;
    scanf("%lld %lld %lld", &n, &m, &k);
    if (k == 1) {
      answer = m - 1;
    } else {
      answer = (k - 1) % (n - m + 1);
      for (long long i = n - m + 2; i <= n; ) {
        if (answer + k >= i) {
          answer = (answer + k) % i;
          ++i;
        } else {
          long long t = min(n - i + 1, (i - answer - 2) / (k - 1));
          answer += t * k;
          i += t;
        }
      }
    }
    printf("Case #%d: %lld\n", ttt, answer + 1);
  }
  return 0;
}

Machining Disc Rotors

答案要么是直径,要么是一对关键点之间的距离。

#include 

using namespace std;

typedef double ld;

const ld pi = acos(-1);

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int tt;
  scanf("%d", &tt);
  for (int ttt = 1; ttt <= tt; ++ttt) {
    map<ld, int> sum;
    int n, radius;
    scanf("%d %d", &n, &radius);
    sum[0] = 0;
    sum[pi * 2] = n;
    while (n--) {
      int x, y, r;
      scanf("%d %d %d", &x, &y, &r);
      if ((r + radius) * (r + radius) <= x * x + y * y || (r - radius) * (r - radius) >= x * x + y * y) {
        continue;
      }
      ld angle = atan2(y, x);
      ld delta = acos((radius * radius + x * x + y * y - r * r) / (2 * radius * sqrt(x * x + y * y)));
      ld left = angle - delta;
      while (left < 0) {
        left += pi * 2;
      }
      while (left >= pi * 2) {
        left -= pi * 2;
      }
      ld right = angle + delta;
      while (right < 0) {
        right += pi * 2;
      }
      while (right >= pi * 2) {
        right -= pi * 2;
      }
      ++sum[left];
      --sum[right];
      if (left > right) {
        ++sum[0];
      }
    }
    int now = 0;
    vector<pair<ld, ld>> segments;
    for (auto p = sum.begin(); p != sum.end(); ++p) {
      now += p->second;
      if (!now) {
        auto q = p;
        ++q;
        segments.emplace_back(p->first, q->first);
      }
    }
    ld answer = 0;
    for (int i = 0; i < segments.size(); ++i) {
      answer = max(answer, sin((segments[i].second - segments[i].first) / 2));
      if (segments[i].second - segments[i].first >= pi) {
        answer = 1;
      }
    }
    for (int i = 0; i < segments.size(); ++i) {
      for (int j = 0; j < i; ++j) {
        answer = max(answer, sin((segments[i].first - segments[j].first) / 2));
        answer = max(answer, sin((segments[i].first - segments[j].second) / 2));
        answer = max(answer, sin((segments[i].second - segments[j].first) / 2));
        answer = max(answer, sin((segments[i].second - segments[j].second) / 2));
        ld low = max(segments[j].first, segments[i].first - pi);
        ld high = min(segments[j].second, segments[i].second - pi);
        if (low <= high) {
          answer = 1;
        }
      }
    }
    printf("Case #%d: %.12lf\n", ttt, answer * radius * 2);
  }
  return 0;
}

Renaissance Past in Nancy

考虑一个物品的多项式是 ( 1 + x b + x 2 b + ⋯ x a b ) (1+x^b+x^{2b}+\cdots x^{ab}) (1+xb+x2b+xab) ,这个东西有逆,查询相当于查询两个前缀多项式的积的前 c c c 项和。

#include 

using namespace std;

const int N = 12345;
const int M = 1234;
const int md = 1e9 + 7;

int n, m, f[N][M], g[N][M];

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);
  for (int ttt = 1; ttt <= tt; ++ttt) {
    printf("Case #%d:\n", ttt);
    scanf("%d %d", &n, &m);
    f[0][0] = g[0][0] = 1;
    for (int i = 1; i <= n; ++i) {
      int a, b;
      scanf("%d %d", &a, &b);
      ++a;
      for (int j = 0; j < b; ++j) {
        for (int k = j; k <= 1000; k += b) {
          f[i][k] = f[i - 1][k];
          if (k >= b) {
            add(f[i][k], f[i][k - b]);
          }
          if (k >= a * b) {
            sub(f[i][k], f[i - 1][k - a * b]);
          }
        }
      }
      for (int k = 0; k <= 1000; ++k) {
        g[i][k] = g[i - 1][k];
        if (k >= b) {
          sub(g[i][k], g[i - 1][k - b]);
        }
      }
      if (a * b <= 1000) {
        for (int j = 0; j < a * b; ++j) {
          for (int k = j; k <= 1000; k += a * b) {
            if (k >= a * b) {
              add(g[i][k], g[i][k - a * b]);
            }
          }
        }
      }
    }
    int answer = 0;
    while (m--) {
      int l, r, c;
      scanf("%d %d %d", &l, &r, &c);
      l = (l + answer) % n + 1;
      r = (r + answer) % n + 1;
      if (l > r) {
        swap(l, r);
      }
      --l;
      int sum = 0;
      for (int i = 0; i <= c; ++i) {
        add(sum, f[r][i]);
      }
      answer = 0;
      for (int i = 0; i <= c; ++i) {
        add(answer, mul(sum, g[l][i]));
        sub(sum, f[r][c - i]);
      }
      printf("%d\n", answer);
    }
  }
  return 0;
}

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