CodeForces 1089 简要题解

Alice the Fan

预处理 f ( w i n a , w i n b , s c o r e a , s c o r e b ) f(win_a, win_b, score_a, score_b) f(wina,winb,scorea,scoreb) 表示这个状态能不能到达然后倒着输出方案就行了。

#include <bits/stdc++.h>

using namespace std;

const int N = 3;
const int MAX = 200;
const int FIRST = 25;
const int LAST = 15;
const int DIFF = 2;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  vector<vector<pair<int, int>>> dp((N + 1) * (N + 1), vector<pair<int, int>> ((MAX + 1) * (MAX + 1), make_pair(-1, -1)));
  vector<pair<int, int>> a_win_first, a_win_last;
  vector<pair<int, int>> b_win_first, b_win_last;
  for (int i = 0; i <= MAX; ++i) {
    for (int j = 0; j <= MAX; ++j) {
      if (abs(i - j) == DIFF) {
        if (max(i, j) >= FIRST) {
          if (i > j) {
            a_win_first.emplace_back(i, j);
          } else {
            b_win_first.emplace_back(i, j);
          }
        }
        if (max(i, j) >= LAST) {
          if (i > j) {
            a_win_last.emplace_back(i, j);
          } else {
            b_win_last.emplace_back(i, j);
          }
        }
      } else if (abs(i - j) > DIFF) {
        if (max(i, j) == FIRST) {
          if (i > j) {
            a_win_first.emplace_back(i, j);
          } else {
            b_win_first.emplace_back(i, j);
          }
        }
        if (max(i, j) == LAST) {
          if (i > j) {
            a_win_last.emplace_back(i, j);
          } else {
            b_win_last.emplace_back(i, j);
          }
        }
      }
    }
  }
  dp[0][0] = make_pair(0, 0);
  for (int win_a = 0; win_a < N; ++win_a) {
    for (int win_b = 0; win_b < N; ++win_b) {
      for (int score_a = 0; score_a <= MAX; ++score_a) {
        for (int score_b = 0; score_b <= MAX; ++score_b) {
          if (~dp[win_a * (N + 1) + win_b][score_a * (MAX + 1) + score_b].first) {
            vector<pair<int, int>> a_win = win_a == N - 1 && win_b == N - 1 ? a_win_last : a_win_first;
            vector<pair<int, int>> b_win = win_a == N - 1 && win_b == N - 1 ? b_win_last : b_win_first;
            for (auto p : a_win) {
              int new_score_a = score_a + p.first;
              int new_score_b = score_b + p.second;
              if (new_score_a <= MAX && new_score_b <= MAX) {
                int index_win = (win_a + 1) * (N + 1) + win_b;
                int index_score = new_score_a * (MAX + 1) + new_score_b;
                dp[index_win][index_score] = p;
              }
            }
            for (auto p : b_win) {
              int new_score_a = score_a + p.first;
              int new_score_b = score_b + p.second;
              if (new_score_a <= MAX && new_score_b <= MAX) {
                int index_win = win_a * (N + 1) + (win_b + 1);
                int index_score = new_score_a * (MAX + 1) + new_score_b;
                dp[index_win][index_score] = p;
              }
            }
          }
        }
      }
    }
  }
  auto print = [&](int win_a, int win_b, int score_a, int score_b) {
    vector<pair<int, int>> match;
    printf("%d:%d\n", win_a, win_b);
    while (win_a || win_b) {
      pair<int, int> p = dp[win_a * (N + 1) + win_b][score_a * (MAX + 1) + score_b];
      match.push_back(p);
      if (p.first > p.second) {
        --win_a;
      } else {
        --win_b;
      }
      score_a -= p.first;
      score_b -= p.second;
    }
    for (int i = match.size() - 1; ~i; --i) {
      printf("%d:%d%c", match[i].first, match[i].second, i ? ' ' : '\n');
    }
  };
  auto solve = [&](int score_a, int score_b) {
    for (int win_a = N; win_a <= N; ++win_a) {
      for (int win_b = 0; win_b < N; ++win_b) {
        if (~dp[win_a * (N + 1) + win_b][score_a * (MAX + 1) + score_b].first) {
          print(win_a, win_b, score_a, score_b);
          return;
        }
      }
    }
    for (int win_a = N - 1; ~win_a; --win_a) {
      for (int win_b = N; win_b <= N; ++win_b) {
        if (~dp[win_a * (N + 1) + win_b][score_a * (MAX + 1) + score_b].first) {
          print(win_a, win_b, score_a, score_b);
          return;
        }
      }
    }
    puts("Impossible");
  };
  int tt;
  scanf("%d", &tt);
  while (tt--) {
    int score_a, score_b;
    scanf("%d %d", &score_a, &score_b);
    solve(score_a, score_b);
  }
  return 0;
}

Bimatching

假设需要匹配 2 2 2 个的点是 A A A 部点,对每个 A A A 部点拆点,并将拆之后的点连一条边,求一般图最大匹配减去 n n n 就是答案。

#include <bits/stdc++.h>

using namespace std;

class dsu_t {
 public:
  vector<int> p;
  int n;

  dsu_t(int n): n(n) {
    p.resize(n);
    for (int i = 0; i < n; ++i) {
      p[i] = i;
    }
  }

  inline int find(int x) {
    while (x != p[x]) {
      x = p[x] = p[p[x]];
    }
    return x;
  }

  inline bool unite(int x, int y) {
    x = find(x);
    y = find(y);
    if (x == y) {
      return false;
    } else {
      p[x] = y;
      return true;
    }
  }
};

pair<int, vector<int>> max_matching(int n, vector<vector<int>> adj) {
  vector<int> match(n, -1);
  int answer = 0;
  auto find_matching = [&](int start) {
    vector<int> color(n, -1);
    vector<int> prev(n, -1);
    vector<int> visit(n);
    queue<int> q;
    int tt = 0;
    dsu_t dsu(n);
    q.push(start);
    color[start] = 0;
    auto lca = [&](int x, int y) {
      ++tt;
      x = dsu.find(x);
      y = dsu.find(y);
      while (true) {
        if (~x) {
          if (visit[x] == tt) {
            return x;
          }
          visit[x] = tt;
          if (~match[x]) {
            x = dsu.find(prev[match[x]]);
          } else {
            x = -1;
          }
        }
        swap(x, y);
      }
    };
    auto blossom = [&](int x, int y, int z) {
      while (dsu.find(x) != z) {
        prev[x] = y;
        if (color[match[x]] == 1) {
          q.push(match[x]);
          color[match[x]] = 0;
        }
        if (dsu.find(x) == x) {
          dsu.unite(x, z);
        }
        if (dsu.find(match[x]) == match[x]) {
          dsu.unite(match[x], z);
        }
        y = match[x];
        x = prev[y];
      }
    };
    while (!q.empty()) {
      int x = q.front();
      q.pop();
      for (auto y : adj[x]) {
        if (!~color[y]) {
          color[y] = 1;
          prev[y] = x;
          if (!~match[y]) {
            while (~x) {
              int last = match[x];
              match[x] = y;
              match[y] = x;
              if (~last) {
                y = last;
                x = prev[y];
              } else {
                break;
              }
            }
            return true;
          } else {
            q.push(match[y]);
            color[match[y]] = 0;
          }
        } else if (!color[y] && dsu.find(x) != dsu.find(y)) {
          int z = lca(x, y);
          blossom(x, y, z);
          blossom(y, x, z);
        }
      }
    }
    return false;
  };
  for (int i = 0; i < n; ++i) {
    if (!~match[i] && find_matching(i)) {
      ++answer;
    }
  }
  return make_pair(answer, match);
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int tt;
  cin >> tt;
  while (tt--) {
    int n, m;
    cin >> n >> m;
    vector<vector<int>> adj(n * 2 + m);
    for (int i = 0; i < n; ++i) {
      adj[i].push_back(i + n);
      adj[i + n].push_back(i);
      string s;
      cin >> s;
      for (int j = 0; j < m; ++j) {
        if (s[j] == '1') {
          adj[i].push_back(j + n * 2);
          adj[j + n * 2].push_back(i);
          adj[i + n].push_back(j + n * 2);
          adj[j + n * 2].push_back(i + n);
        }
      }
    }
    printf("%d\n", max_matching(n * 2 + m, adj).first - n);
  }
  return 0;
}

Cactus Search

预处理两两最短路,每次选择一个最优的使候选点数最小的点询问即可,每次候选点数至少会变成原来的一半。

#include <bits/stdc++.h>

using namespace std;

int main() {
  int n, m;
  cin >> n >> m;
  vector<vector<int>> dist(n, vector<int> (n, n));
  vector<vector<int>> adj(n);
  for (int i = 0; i < n; ++i) {
    dist[i][i] = 0;
  }
  while (m--) {
    int l;
    cin >> l;
    int last = -1;
    while (l--) {
      int x;
      cin >> x;
      --x;
      if (~last) {
        adj[last].push_back(x);
        adj[x].push_back(last);
        dist[x][last] = dist[last][x] = 1;
      }
      last = x;
    }
  }
  for (int k = 0; k < n; ++k) {
    for (int i = 0; i < n; ++i) {
      for (int j = 0; j < n; ++j) {
        dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
      }
    }
  }
  for (int tt = 0; tt < n; ++tt) {
    vector<int> candidates(n);
    for (int i = 0; i < n; ++i) {
      candidates.push_back(i);
    }
    while (true) {
      int root = -1;
      int remain = n;
      for (int x = 0; x < n; ++x) {
        int most = 0;
        for (auto y : adj[x]) {
          int current = 0;
          for (auto z : candidates) {
            if (dist[x][z] > dist[y][z]) {
              ++current;
            }
          }
          most = max(most, current);
        }
        if (remain > most) {
          remain = most;
          root = x;
        }
      }
      cout << root + 1 << endl;
      string type;
      cin >> type;
      if (type == "FOUND") {
        break;
      }
      int result;
      cin >> result;
      --result;
      vector<int> new_candidates;
      for (auto x : candidates) {
        if (dist[root][x] > dist[result][x]) {
          new_candidates.push_back(x);
        }
      }
      swap(candidates, new_candidates);
    }
  }
  return 0;
}

Distance Sum

特判树的情况,将所有度数为 1 1 1 的点全部缩掉,将度数大于 2 2 2 的点设为关键点,在关键点之间跑最短路,然后枚举两条边计算贡献即可。

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n, m;
  scanf("%d %d", &n, &m);
  vector<set<int>> graph(n);
  for (int i = 0; i < m; ++i) {
    int x, y;
    scanf("%d %d", &x, &y);
    --x;
    --y;
    graph[x].insert(y);
    graph[y].insert(x);
  }
  set<pair<int, int>> degrees;
  vector<int> weight(n, 1);
  long long answer = 0;
  for (int i = 0; i < n; ++i) {
    degrees.insert(make_pair(graph[i].size(), i));
  }
  while (degrees.size() > 1) {
    int x = degrees.begin()->second;
    if (graph[x].size() > 1) {
      break;
    }
    int y = *graph[x].begin();
    degrees.erase(make_pair(graph[x].size(), x));
    degrees.erase(make_pair(graph[y].size(), y));
    graph[x].erase(y);
    graph[y].erase(x);
    answer += 2ll * weight[x] * (n - weight[x]);
    weight[y] += weight[x];
    degrees.insert(make_pair(graph[y].size(), y));
  }
  if (degrees.size() == 1) {
    printf("%lld\n", answer >> 1);
    return 0;
  }
  vector<int> id(n, -1);
  vector<vector<int>> adj(n);
  m = 0;
  for (int x = 0; x < n; ++x) {
    for (auto y : graph[x]) {
      adj[x].push_back(y);
    }
    if (adj[x].size() > 2) {
      id[x] = m++;
    }
  }
  if (!m) {
    for (int i = 0; i < n; ++i) {
      if (adj[i].size() == 2) {
        id[i] = m++;
        break;
      }
    }
  }
  vector<vector<vector<vector<int>>>> between(m, vector<vector<vector<int>>> (m));
  for (int x = 0; x < n; ++x) {
    if (~id[x]) {
      for (auto y : adj[x]) {
        if (~id[y]) {
          between[id[x]][id[y]].emplace_back();
        }
      }
    }
  }
  for (int i = 0; i < n; ++i) {
    if (adj[i].size() == 2 && !~id[i]) {
      vector<int> weights(1, weight[i]);
      vector<int> end(2);
      id[i] = m;
      for (int j = 0; j < 2; ++j) {
        int x = adj[i][j], prev = i;
        while (!~id[x]) {
          weights.push_back(weight[x]);
          id[x] = m;
          int next = adj[x][0] + adj[x][1] - prev;
          prev = x;
          x = next;
        }
        end[j] = x;
        reverse(weights.begin(), weights.end());
      }
      between[id[end[1]]][id[end[0]]].push_back(weights);
    }
  }
  {
    vector<int> new_weight(m);
    for (int i = 0; i < n; ++i) {
      if (id[i] != -1 && id[i] < m) {
        new_weight[id[i]] = weight[i];
      }
    }
    weight = new_weight;
  }
  vector<vector<int>> dist(m, vector<int> (m, n));
  for (int i = 0; i < m; ++i) {
    dist[i][i] = 0;
  }
  vector<pair<int, int>> non_empty;
  for (int i = 0; i < m; ++i) {
    for (int j = 0; j < m; ++j) {
      for (auto &p : between[i][j]) {
        dist[i][j] = dist[j][i] = min(dist[i][j], (int)p.size() + 1);
      }
      if (!between[i][j].empty()) {
        non_empty.emplace_back(i, j);
      }
    }
  }
  for (int k = 0; k < m; ++k) {
    for (int i = 0; i < m; ++i) {
      for (int j = 0; j < m; ++j) {
        dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
      }
    }
  }
  for (int i = 0; i < m; ++i) {
    for (int j = 0; j < i; ++j) {
      answer += 2ll * weight[i] * weight[j] * dist[i][j];
    }
  }
  vector<vector<vector<vector<long long>>>> sum_weight(m, vector<vector<vector<long long>>> (m));
  vector<vector<vector<vector<long long>>>> sum_dist(m, vector<vector<vector<long long>>> (m));
  for (int i = 0; i < m; ++i) {
    for (int j = 0; j < m; ++j) {
      sum_weight[i][j].resize(between[i][j].size());
      sum_dist[i][j].resize(between[i][j].size());
      for (int k = 0; k < between[i][j].size(); ++k) {
        sum_weight[i][j][k].resize(between[i][j][k].size() + 1);
        sum_dist[i][j][k].resize(between[i][j][k].size() + 1);
        for (int l = 0; l < between[i][j][k].size(); ++l) {
          sum_weight[i][j][k][l + 1] = sum_weight[i][j][k][l] + between[i][j][k][l];
          sum_dist[i][j][k][l + 1] = sum_dist[i][j][k][l] + (long long)between[i][j][k][l] * l;
        }
      }
    }
  }
  auto solve = [&](int i, int j, int k, int l, int r, int dist, int diff) {
    return dist * (sum_weight[i][j][k][r] - sum_weight[i][j][k][l]) + diff * (sum_dist[i][j][k][r] - sum_dist[i][j][k][l]);
  };
  for (int i = 0; i < m; ++i) {
    for (int j = 0; j < m; ++j) {
      for (int k = 0; k < between[i][j].size(); ++k) {
        for (int l = 0; l < between[i][j][k].size(); ++l) {
          long long w = between[i][j][k][l];
          for (int ii = 0; ii < m; ++ii) {
            answer += 2ll * w * weight[ii] * min(dist[i][ii] + l + 1, dist[j][ii] + (int)between[i][j][k].size() - l);
          }
          for (auto p : non_empty) {
            int ii = p.first;
            int jj = p.second;
            for (int kk = 0; kk < between[ii][jj].size(); ++kk) {
              if (i == ii && j == jj && k == kk) {
                {
                  int inside = l;
                  int outside = dist[i][j] + between[i][j][k].size() - l + 1;
                  if (inside <= outside) {
                    answer += w * solve(i, j, k, 0, l, inside, -1);
                  } else {
                    int ll = inside - outside + 1 >> 1;
                    answer += w * solve(i, j, k, 0, ll, outside, 1);
                    answer += w * solve(i, j, k, ll, l, inside, -1);
                  }
                }
                {
                  int inside = -l;
                  int outside = dist[i][j] + between[i][j][k].size() + l + 1;
                  int ll = min((int)between[i][j][k].size(), outside - inside + 1 >> 1);
                  answer += w * solve(i, j, k, l, ll, inside, 1);
                  answer += w * solve(i, j, k, ll, between[i][j][k].size(), outside, -1);
                }
              } else {
                int to_ii = min(dist[i][ii] + l + 1, dist[j][ii] + (int)between[i][j][k].size() - l);
                int to_jj = min(dist[i][jj] + l + 1, dist[j][jj] + (int)between[i][j][k].size() - l);
                int ll = min(between[ii][jj][kk].size(), to_jj - to_ii + between[ii][jj][kk].size() + 1 >> 1);
                answer += w * solve(ii, jj, kk, 0, ll, to_ii + 1, 1);
                answer += w * solve(ii, jj, kk, ll, between[ii][jj][kk].size(), to_jj + between[ii][jj][kk].size(), -1);
              }
            }
          }
        }
      }
    }
  }
  printf("%lld\n", answer >> 1);
  return 0;
}

Easy Chess

在前 7 7 7 行不以最后一列结尾,随便构造一下就行了。

#include <bits/stdc++.h>

using namespace std;

const int N = 8;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  n -= 2;
  vector<int> a(N);
  a[0] = 1;
  a[N - 1] = 2;
  while (n) {
    for (int i = 0; i < N; ++i) {
      if (a[i] < N) {
        ++a[i];
        break;
      }
    }
    --n;
  }
  int column = 0;
  vector<pair<int, int>> answer;
  for (int i = 0; i < N; ++i) {
    if (a[i]) {
      answer.emplace_back(i, column);
      --a[i];
      if (a[i] == N - 1) {
        if (i == N - 1) {
          for (int j = 0; j < N; ++j) {
            if (j != column) {
              answer.emplace_back(i, j);
            }
          }
        } else {
          for (int j = N - 1; ~j; --j) {
            if (j != column) {
              answer.emplace_back(i, j);
            }
          }
          column = !column;
        }
      } else {
        if (i == N - 1) {
          for (int j = 0; j < N && a[i] > 1; ++j) {
            if (j != column) {
              answer.emplace_back(i, j);
              --a[i];
            }
          }
          answer.emplace_back(i, N - 1);
        } else {
          for (int j = 0; j < N && a[i]; ++j) {
            if (j != column) {
              answer.emplace_back(i, j);
              --a[i];
              if (!a[i]) {
                column = j;
              }
            }
          }
        }
      }
    }
  }
  for (int i = 0; i < answer.size(); ++i) {
    printf("%c%d%c", answer[i].first + 'a', answer[i].second + 1, i == answer.size() - 1 ? '\n' : ' ');
  }
  return 0;
}

Fractions

如果 n n n p k p^k pk ,那么无解,否则考虑 n = x y ( gcd ⁡ ( x , y ) = 1 ) n=xy(\gcd(x,y)=1) n=xy(gcd(x,y)=1) ,用 a x + b y = n − 1 n \frac{a}{x}+\frac{b}{y}=\frac{n-1}{n} xa+yb=nn1 构造解,容易证明一定有解。

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  for (int i = 2; i * i < n; ++i) {
    if (n % i == 0 && __gcd(i, n / i) == 1) {
      int x = i, y = n / i;
      for (int a = 1; a < x; ++a) {
        if ((n - 1 - a * y) % x == 0) {
          int b = (n - 1 - a * y) / x;
          puts("YES");
          puts("2");
          printf("%d %d\n", a, x);
          printf("%d %d\n", b, y);
          return 0;
        }
      }
    }
  }
  puts("NO");
  return 0;
}

Guest Student

暴力枚举起始位置算即可。

#include <bits/stdc++.h>

using namespace std;

const int N = 7;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int tt;
  scanf("%d", &tt);
  while (tt--) {
    vector<int> a(N);
    int n;
    scanf("%d", &n);
    int each = 0;
    for (int i = 0; i < N; ++i) {
      scanf("%d", &a[i]);
      each += a[i];
    }
    int week = (n - 1) / each;
    int answer = N;
    for (int i = 0; i < N; ++i) {
      int remain = n - week * each;
      for (int j = 1; j <= N; ++j) {
        if (a[(i + j) % N]) {
          --remain;
          if (!remain) {
            answer = min(answer, j);
            break;
          }
        }
      }
    }
    printf("%d\n", week * N + answer);
  }
  return 0;
}

Harder Satisfiability

建出2-SAT,不合法当且仅当满足下面某个条件:

  • 2-SAT无解。
  • 存在 i &lt; j i&lt;j i<j i i i ∃ \exist j j j ∀ \forall ,并且 i i i j j j 在同一个强连通分量里。
  • 存在两个点 i , j i, j i,j 都是 ∀ \forall ,且它们一个可以到达另一个。

显然不满足任意一个时是不合法的,否则可以用类似2-SAT的构造求出解。

论文链接

#include <bits/stdc++.h>

using namespace std;

bool solve() {
  int n, m;
  cin >> n >> m;
  string s;
  cin >> s;
  vector<int> from(m), to(m);
  for (int i = 0; i < m; ++i) {
    int x, y;
    cin >> x >> y;
    if (x < 0) {
      x = n - x;
    }
    if (y < 0) {
      y = n - y;
    }
    --x;
    --y;
    from[i] = x;
    to[i] = y;
    if (from[i] > to[i]) {
      swap(from[i], to[i]);
    }
  }
  auto get_another = [&](int x) {
    return x >= n ? x - n : x + n;
  };
  vector<vector<int>> adj(n << 1);
  for (int i = 0; i < m; ++i) {
    adj[get_another(from[i])].push_back(to[i]);
    adj[get_another(to[i])].push_back(from[i]);
  }
  vector<int> dfn(n << 1, -1), low(n << 1, -1), scc(n << 1, -1);
  stack<int> st;
  int cc = 0, tt = 0;
  function<void(int)> tarjan = [&](int x) {
    dfn[x] = low[x] = tt++;
    st.push(x);
    for (auto y : adj[x]) {
      if (!~dfn[y]) {
        tarjan(y);
        low[x] = min(low[x], low[y]);
      } else if (!~scc[y]) {
        low[x] = min(low[x], dfn[y]);
      }
    }
    if (dfn[x] == low[x]) {
      for (int t = -1; t != x; ) {
        t = st.top();
        scc[t] = cc;
        st.pop();
      }
      ++cc;
    }
  };
  for (int i = 0; i < n << 1; ++i) {
    if (!~dfn[i]) {
      tarjan(i);
    }
  }
  for (int i = 0; i < n; ++i) {
    if (scc[i] == scc[i + n]) {
      return false;
    }
  }
  vector<bool> ban_scc(cc);
  for (int i = 0; i < n; ++i) {
    if (s[i] == 'E') {
      ban_scc[scc[i]] = ban_scc[scc[i + n]] = true;
    } else {
      if (ban_scc[scc[i]] || ban_scc[scc[i + n]]) {
        return false;
      }
    }
  }
  for (int i = 0; i < cc; ++i) {
    ban_scc[i] = false;
  }
  for (int i = 0; i < n; ++i) {
    if (s[i] == 'A') {
      if (ban_scc[scc[i]]) {
        return false;
      }
      ban_scc[scc[i]] = true;
      if (ban_scc[scc[i + n]]) {
        return false;
      }
      ban_scc[scc[i + n]] = true;
    }
  }
  vector<bool> can(cc);
  vector<vector<int>> graph(cc);
  for (int x = 0; x < n << 1; ++x) {
    for (auto y : adj[x]) {
      if (scc[x] != scc[y]) {
        graph[scc[x]].push_back(scc[y]);
      }
    }
  }
  for (int x = 0; x < cc; ++x) {
    for (auto y : graph[x]) {
      if (can[y] || ban_scc[y]) {
        can[x] = true;
      }
    }
    if (can[x] && ban_scc[x]) {
      return false;
    }
  }
  return true;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(0);
  cin.tie(0);
  int tt;
  cin >> tt;
  while (tt--) {
    puts(solve() ? "TRUE" : "FALSE");
  }
  return 0;
}

Interval-Free Permutations

考虑用总的方案数减去不合法的方案数,对于不合法的方案,用最少的极长连续段覆盖序列(每个序列至少覆盖一次),如果两个连续段有交,那么它们一定可以合并成一个连续的大段,所以最后有两种情况:

  • 划分段数为 2 2 2 ,那么一定是一个前缀或者一个后缀是 1 , 2 , ⋯ &ThinSpace; , k 1, 2, \cdots, k 1,2,,k
  • 划分段数大于 2 2 2 ,每个位置恰好被覆盖一次。

然后用DP算方案数即可。

你也可以选择OEIS+打表

#include <bits/stdc++.h>

using namespace std;

const int N = 400;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int tt, md;
  scanf("%d %d", &tt, &md);
  auto add = [&](int &x, int y) {
    x += y;
    if (x >= md) {
      x -= md;
    }
  };
  auto sub = [&](int &x, int y) {
    x -= y;
    if (x < 0) {
      x += md;
    }
  };
  auto mul = [&](int x, int y) {
    return (long long)x * y % md;
  };
  vector<int> fact(N + 1);
  fact[0] = 1;
  for (int i = 0; i < N; ++i) {
    fact[i + 1] = mul(fact[i], i + 1);
  }
  vector<int> ways(N + 1);
  for (int i = 1; i <= N; ++i) {
    ways[i] = fact[i];
    for (int j = 1; j < i; ++j) {
      sub(ways[i], mul(ways[j], fact[i - j]));
    }
  }
  vector<vector<int>> dp(N + 1, vector<int> (N + 1));
  dp[0][0] = 1;
  for (int i = 1; i <= N; ++i) {
    for (int j = 1; j <= i; ++j) {
      for (int k = 1; k <= i; ++k) {
        add(dp[i][j], mul(dp[i - k][j - 1], fact[k]));
      }
    }
  }
  vector<int> f(N + 1);
  for (int i = 1; i <= N; ++i) {
    f[i] = fact[i];
    for (int j = 1; j < i; ++j) {
      sub(f[i], mul(2, mul(ways[j], fact[i - j])));
    }
    for (int j = 3; j < i; ++j) {
      sub(f[i], mul(dp[i][j], f[j]));
    }
  }
  f[2] = 2;
  while (tt--) {
    int n;
    scanf("%d", &n);
    printf("%d\n", f[n]);
  }
  return 0;
}

JS Minification

模拟,最后加空格的时候贪心即可。

#include <bits/stdc++.h>

using namespace std;

const int MAX = 20;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  {
    string foo;
    getline(cin, foo);
    stringstream ss;
    ss << foo;
    ss >> n;
  }
  set<string> reserved;
  {
    string foo;
    getline(cin, foo);
    stringstream ss;
    ss << foo;
    for (int i = 0; i < n; ++i) {
      string bar;
      ss >> bar;
      reserved.insert(bar);
    }
  }
  int m;
  {
    string foo;
    getline(cin, foo);
    stringstream ss;
    ss << foo;
    ss >> m;
  }
  vector<string> tokens;
  auto parse_reserved = [&](string s, int start) {
    int result = 0;
    string current;
    for (int i = start; i < start + MAX && i < s.length() && s[i] != '#'; ++i) {
      current += s[i];
      if (reserved.find(current) != reserved.end()) {
        result = i - start + 1;
      }
    }
    return result;
  };
  auto parse_number = [&](string s, int start) {
    int result = 0;
    while (start + result < s.length() && isdigit(s[start + result])) {
      ++result;
    }
    return result;
  };
  auto parse_word = [&](string s, int start) {
    if (isdigit(s[start])) {
      return 0;
    }
    int result = 0;
    auto is_valid = [&](char c) {
      return isdigit(c) || isalpha(c) || c == '_' || c == '$';
    };
    while (start + result < s.length() && is_valid(s[start + result])) {
      ++result;
    }
    return result;
  };
  auto parse = [&](string s, int start) {
    return max(max(parse_reserved(s, start), parse_number(s, start)), parse_word(s, start));
  };
  while (m--) {
    string foo;
    getline(cin, foo);
    for (int i = 0; i < foo.length(); ++i) {
      if (foo[i] == ' ') {
        continue;
      }
      if (foo[i] == '#') {
        break;
      }
      int length = parse(foo, i);
      tokens.push_back(foo.substr(i, length));
      i += length - 1;
    }
  }
  map<string, string> changed;
  string last = "";
  auto find_next = [&]() {
    do {
      int i = last.length() - 1;
      while (~i && last[i] == 'z') {
        --i;
      }
      if (~i) {
        ++last[i++];
        while (i < last.length()) {
          last[i++] = 'a';
        }
      } else {
        last = string(last.length() + 1, 'a');
      }
    } while (reserved.find(last) != reserved.end());
  };
  for (auto &s : tokens) {
    if (reserved.find(s) == reserved.end() && !isdigit(s[0])) {
      if (changed.find(s) == changed.end()) {
        find_next();
        changed[s] = last;
      }
      s = changed[s];
    }
  }
  int p = tokens.size();
  for (int i = 0; i < tokens.size(); ++i) {
    string foo = tokens[i];
    for (int j = i + 1; j < tokens.size(); ++j) {
      foo += tokens[j];
      if (parse(foo, 0) != tokens[i].length()) {
        p = min(p, j - 1);
        break;
      }
      if (foo.length() > MAX) {
        break;
      }
    }
    cout << tokens[i];
    if (i == p) {
      cout << " ";
      p = tokens.size();
    }
  }
  cout << endl;
  return 0;
}

King Kog’s Reception

就是求 max ⁡ t j + ∑ t j ≤ t i ≤ t d i \max t_j + \sum_{t_j\le t_i\le t} d_i maxtj+tjtitdi ,线段树维护即可。

#include <bits/stdc++.h>

using namespace std;

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

  node_t unite(const node_t &l, const node_t &r) {
    node_t result;
    result.value = min(l.value, r.value);
    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);
    } else {
      tree[x].value = -l;
    }
  }

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

const int MAX = 1000000;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  segtree_t segtree(MAX + 1);
  segtree.build(0, 0, MAX);
  int tt;
  cin >> tt;
  vector<pair<int, int>> a(tt);
  for (int qq = 0; qq < tt; ++qq) {
    string type;
    cin >> type;
    if (type == "+") {
      int t, d;
      cin >> t >> d;
      a[qq] = make_pair(t, d);
      segtree.modify(t, MAX, d);
    } else if (type == "-") {
      int i;
      cin >> i;
      --i;
      int t = a[i].first, d = a[i].second;
      segtree.modify(t, MAX, -d);
    } else {
      int t;
      cin >> t;
      printf("%lld\n", segtree.query(t, t).value - segtree.query(0, t - 1).value + 1);
    }
  }
  return 0;
}

Lazyland

贪心。

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n, m;
  scanf("%d %d", &n, &m);
  vector<pair<int, int>> a(n);
  for (int i = 0; i < n; ++i) {
    scanf("%d", &a[i].first);
  }
  for (int i = 0; i < n; ++i) {
    scanf("%d", &a[i].second);
  }
  sort(a.begin(), a.end());
  int need = m;
  vector<int> choice;
  for (int i = 0, j = 0; i < n; i = j) {
    while (j < n && a[j].first == a[i].first) {
      ++j;
    }
    for (int k = i; k + 1 < j; ++k) {
      choice.push_back(a[k].second);
    }
    --need;
  }
  sort(choice.begin(), choice.end());
  long long answer = 0;
  for (int i = 0; i < need; ++i) {
    answer += choice[i];
  }
  printf("%lld\n", answer);
  return 0;
}

Minegraphed

具体构造见代码。

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  cin >> n;
  vector<vector<string>> board(3, vector<string> (n * 3, string(n * 3, '#')));
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < n * 3; ++j) {
      board[0][i * 3][j] = board[2][j][i * 3] = '.';
    }
    board[1][i * 3][i * 3 + 1] = board[1][i * 3 + 1][i * 3 + 1] = board[2][i * 3 + 1][i * 3 + 1] = '.';
    for (int j = 0; j < n; ++j) {
      int can;
      cin >> can;
      if (can) {
        for (int k = 0; k < 3; ++k) {
          board[k][j * 3 + 1][i * 3 + 1] = '.';
        }
      }
    }
    board[0][i * 3][n * 3 - 1] = i + '1';
  }
  cout << n * 3 << " " << n * 3 << " " << 3 << endl;
  for (int i = 2; ~i; --i) {
    for (int j = 0; j < n * 3; ++j) {
      cout << board[i][j] << endl;
    }
    if (i) {
      cout << endl;
    }
  }
  return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值