其他分享
首页 > 其他分享> > 20220823 模拟赛题解

20220823 模拟赛题解

作者:互联网

T1 文件压缩

Decription

link

Solution

可以根据 \(S'\) 和 \(p\) 求出第一个字符,然后把 \(S'\) sort 一遍后得到字符串 \(T\),那么我们就可以求出每一个字符的前驱和后继,所以从第一个字符开始跑,就可以根据这些关系求出原字符串,这样肯定是正确的。

时间复杂度:\(O(n^2)\)。

Code

代码
#include <bits/stdc++.h>

#define db(x) cerr << #x << '=' << x << endl
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define dbg debug("*** Passing [%s] in LINE %d\n", __FUNCTION__, __LINE__)

using namespace std;

const int kMaxN = 1e4 + 5;

int n, p, idx;
char fir, c1[kMaxN], c2[kMaxN], ans[kMaxN];
string s;
bool vis[kMaxN];

int main() {
  cin >> n >> s >> p;
  s = " " + s;
  for (int i = 1; i <= n; ++i) {
    c1[i] = c2[i] = s[i];
  }
  fir = c1[p];
  sort(c2 + 1, c2 + 1 + n);
  for (int i = 1; i <= n; ++i) {
    if (c2[i] == fir) {
      idx = i; break ;
    }
  }
  ans[1] = c1[idx]; vis[idx] = 1;
  for (int i = 2; i <= n; ++i) {
    for (int j = n; j; --j) {
      if (!vis[j] && c2[j] == c1[idx]) {
        ans[i] = c1[j];
        idx = j;
        vis[idx] = 1;
        break ;
      }
    }
  }
  for (int i = n; i; --i) {
    cout << ans[i];
  }
  return 0;
}

T2 [AGC018D] Tree and Hamilton Path

Decription

link

Solution

先求出哈密尔顿回路,再减去回路中最短一条路径就是最长的哈密尔顿路径了。

哈密尔顿回路直接求肯定不好求,所以可以求出每条边对于整个回路的贡献。
假设当前边为 \(\{u,v\}\)(\(u\) 是 \(v\) 的父亲),权值为 \(w\),可以发现这条边最多经过 \(2\times\min\{sz[v],n-sz[v]\}\) 次,至于构造,就是它从下边过去又从上边回来,那么至多也只能经过这么多次。然后可以发现这样是可行的。

然后考虑删哪条边。
这里可以《容 易》地发现,如果当前点不是重心,那么必有一个子树的 \(sz>\frac{n}{2}\) 那么这时就会有一个完整的路径在那个最大的子树内,则这条路径不会经过 \(u\)。所以要删的边必须至少有一边是重心,那么取重心连出来的最短的边就行了。

如果有两个重心,则只能删这两个重心之间的边。

时间复杂度:\(O(n)\)。

Code

代码
#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const int kMaxN = 1e5 + 5;

struct Edge {
  int v, w;

  Edge() {}
  Edge(int _v, int _w) : v(_v), w(_w) {}
  ~Edge() {}
};

int n, mi;
ll ans;
int sz[kMaxN];
vector<int> ctr;
vector<Edge> G[kMaxN];

void dfs(int u, int fa) {
  sz[u] = 1;
  int mx = 0;
  for (auto [v, w] : G[u]) {
    if (v == fa) continue ;
    dfs(v, u);
    sz[u] += sz[v], mx = max(mx, sz[v]), ans += 2ll * w * min(sz[v], n - sz[v]);
  }
  mx = max(mx, n - sz[u]);
  if (mx <= n / 2) ctr.emplace_back(u);
}

int main() {
  cin >> n;
  for (int i = 1, u, v, w; i < n; ++i) {
    cin >> u >> v >> w;
    G[u].emplace_back(Edge(v, w));
    G[v].emplace_back(Edge(u, w));
  }
  dfs(1, 0);
  if (ctr.size() >= 2) {
    for (auto [v, w] : G[ctr[0]]) {
      if (v == ctr[1]) {
        ans -= w; break ;
      }
    }
  } else {
    mi = 1e9;
    for (auto [v, w] : G[ctr[0]]) {
      mi = min(mi, w);
    }
    ans -= mi;
  }
  cout << ans << endl;
  return 0;
}

T3 [USACO20JAN]Springboards G

Description

link

Solution

可以写出 \(30\) 分暴力。

优化:把每个跳板拆成两个点,分别是它的起点和终点,把这些点按 \(x\) 排序,然后就可以树状数组优化了。那么在起点的地方转移,在终点的地方加入树状数组即可。

时间复杂度:\(O(n\log n)\)。

Code

代码
#include <bits/stdc++.h>

#define int long long
#define db(x) cerr << #x << '=' << x << endl
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define dbg debug("*** Passing [%s] in LINE %d\n", __FUNCTION__, __LINE__)

using namespace std;

const int kMaxP = 2e5 + 5;

class BIT {
  public:
    void upd(int x, int v) {
      for (; x <= n; x += x & -x) {
        c[x] = max(c[x], v);
      }
    }
    int qry(int x) {
      int ret = 0;
      for (; x; x -= x & -x) {
        ret = max(ret, c[x]);
      }
      return ret;
    }

    BIT() {}
    BIT(int _n) : n(_n) {}
    ~BIT() {}
  private:
    int n, c[kMaxP << 2];
} bit;

struct Node {
  int x, y, id, op;

  Node() {}
  Node(int _x, int _y, int _id, int _op) : x(_x), y(_y), id(_id), op(_op) {}
  ~Node() {}

  friend bool operator < (const Node& n1, const Node& n2) {
    if (n1.x != n2.x) return n1.x < n2.x;
    return n1.y < n2.y;
  }
} a[kMaxP << 1];

int n, p, tot, ans, tt;
int b[kMaxP << 2], xx[kMaxP], xy[kMaxP], yx[kMaxP], yy[kMaxP], f[kMaxP], dis[kMaxP];

signed main() {
  cin >> n >> p;
  for (int i = 1; i <= p; ++i) {
    cin >> xx[i] >> xy[i] >> yx[i] >> yy[i];
    a[++tot] = Node(xx[i], xy[i], i, 0);
    a[++tot] = Node(yx[i], yy[i], i, 1);
    b[++tt] = xy[i];
    b[++tt] = yy[i];
    dis[i] = abs(xx[i] - yx[i]) + abs(xy[i] - yy[i]);
  }
  sort(a + 1, a + 1 + tot), sort(b + 1, b + 1 + tt);
  tt = unique(b + 1, b + 1 + tt) - (b + 1);
  for (int i = 1; i <= tot; ++i) {
    a[i].y = lower_bound(b + 1, b + 1 + tt, a[i].y) - b;
  }
  bit = BIT(tt);
  for (int i = 1; i <= tot; ++i) {
    if (!a[i].op) {
      f[a[i].id] = max(f[a[i].id], bit.qry(a[i].y) + dis[a[i].id]);
      ans = max(ans, f[a[i].id]);
    } else {
      bit.upd(a[i].y, f[a[i].id]);
    }
  }
  // db(ans);
  cout << 2 * n - ans << endl;
  return 0;
}

T4 [AHOI2018初中组]球球的排列

Description

link

Solution

容易发现一个性质:如果 \(x\times y\) 和 \(y\times z\) 都是完全平方数,那么 \(x\times z\) 也是完全平方数。

那么我们就可以将这 \(n\) 个数看成 \(n\) 个不同颜色的球,相同颜色的球两两乘积就是完全平方数,那么用个并查集就可以暴力 \(O(n^2)\) 预处理出来了。

然后这个东西就转换成了:有 \(n\) 个球,一共有 \(m\) 个颜色,求相同球颜色都不相同的排列数。

这个东西直接不好做,所以考虑容斥。

不妨设 \(a_i\) 表示颜色为 \(i\) 的球的个数。
设 \(f(s)\) 表示有至少 \(s\) 对相邻的球颜色相同的方案数,那么答案就是:

\[\sum_{i=0}^{n}{f(i)\cdot (-1)^i} \]

我们可以将当前序列分为连续的颜色相同的若干块,设 \(b_i\) 表示颜色为 \(i\) 的块的个数。所以当前情况的答案就是:

\[\dfrac{(\sum{b_i})!}{\prod{(b_i!)}}\cdot \prod_{i=1}^{m}{(a_i!\cdot C_{a_i-1}^{b_i-1})} \]

这里的 \(\dfrac{(\sum{b_i})!}{\prod{(b_i!)}}\) 就表示整块整块地进行排序的方案数,\(a_i!\) 就是每种颜色的排序,\(C_{a_i-1}^{b_i-1}\) 就是插板法求出每种颜色的块的分配方案。

考虑一件事:如果恰有 \(s\) 对相邻的球颜色相同,那么 \(\sum{b_i}=n-s\)。

所以上面那个式子就可以转化为:

\[(n-s)!\cdot \prod_{i=1}^{m}{(\dfrac{a_i!\cdot C_{a_i-1}^{b_i-1}}{b_i!})} \]

所以我们发现只要求出 \(b_i\),整个方案数就能确定,所以对于 \(\prod\limits_{i=1}^{m}{(\dfrac{a_i!\cdot C_{a_i-1}^{b_i-1}}{b_i!})}\)
进行 dp 即可。

设 \(f[i][j]\) 表示前 \(i\) 种颜色,至多 \(j\) 对相邻且颜色相等的块的个数,这里只要枚举 \(b_i\) 就可以进行转移:

\[f[i][j]=\sum_{k=1}^{min\{a_i,j\}}{f[i-1][j-k]\cdot \dfrac{a_i!\cdot C_{a_i-1}^{k-1}}{k!}} \]

最后答案就是 \(\sum\limits_{i=0}^{n}{f[n][i]\cdot (-1)^i}\)。

时间复杂度:\(O(n^2)\)。

Code

代码
#include <bits/stdc++.h>

#define db(x) cerr << #x << '=' << x << endl
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define dbg debug("*** Passing [%s] in LINE %d\n", __FUNCTION__, __LINE__)
#define summary debug("----------- End -----------\n"), \
                debug("Memory %.5lfMB\n", fabs(&m_ed - &m_st) / 1048576), \
                debug("Time %.2lfs\n", clock() * 1.0 / CLOCKS_PER_SEC)

using namespace std;

bool m_st;

/* ---------- Line ---------- */

typedef long long ll;

const int kMaxN = 305, kMod = 1e9 + 7;

class UFS {
  public:
    void init() {
      iota(fa + 1, fa + 1 + n, 1);
    }
    int find(int x) {
      return x == fa[x] ? x : fa[x] = find(fa[x]);
    }
    void unionn(int x, int y) {
      int fx = find(x), fy = find(y);
      if (fx != fy) fa[fx] = fy;
    }

    UFS() {}
    UFS(int _n) : n(_n) {}
    ~UFS() {}
  private:
    int n, fa[kMaxN];
} s;

int n, tot, ans;
int p[kMaxN], f[kMaxN][kMaxN], a[kMaxN], idx[kMaxN];
int fac[kMaxN], ifac[kMaxN];

bool check(ll x) {
  int tmp = sqrt(x);
  // if (x == 16) db(tmp);
  return 1ll * tmp * tmp == x;
}

int qpow(int bs, int idx) {
  int ret = 1;
  for (; idx; idx >>= 1, bs = 1ll * bs * bs % kMod) {
    if (idx & 1) ret = 1ll * ret * bs % kMod;
  }
  return ret % kMod;
}

int inv(int x) {
  return qpow(x, kMod - 2);
}

int C(int m, int n) {
  return 1ll * fac[m] * ifac[n] % kMod * ifac[m - n] % kMod;
}

int H(int a, int b) {
  return 1ll * fac[a] * C(a - 1, b - 1) % kMod * ifac[b] % kMod;
}

/* ---------- Line ---------- */

bool m_ed;

int main() {
  cin >> n;
  for (int i = 1; i <= n; ++i) {
    cin >> p[i];
  }

  s = UFS(n), s.init();
  for (int i = 1; i <= n; ++i) {
    for (int j = i + 1; j <= n; ++j) {
      if (check(1ll * p[i] * p[j])) {
        s.unionn(i, j);
        // dbg;
      }
    }
  }
  // db(check(p[2] * p[6]));
  // db(check(16));
  for (int i = 1; i <= n; ++i) {
    int fi = s.find(i);
    if (!idx[fi]) {
      idx[fi] = ++tot;
    }
    ++a[idx[fi]];
  }
  fac[0] = ifac[0] = 1;
  for (int i = 1; i <= n; ++i) {
    fac[i] = 1ll * fac[i - 1] * i % kMod;
    ifac[i] = inv(fac[i]);
    // db(i), db(fac[i]), db(ifac[i]);
  }
  // db(tot);

  f[0][0] = 1;
  for (int i = 1; i <= tot; ++i) {
    for (int j = 1; j <= n; ++j) {
      for (int k = 1; k <= min(j, a[i]); ++k) {
        f[i][j] = (f[i][j] + 1ll * f[i - 1][j - k] * H(a[i], k) % kMod) % kMod;
      }
    }
  }
  for (int i = 0; i <= n; ++i) {
    int fl = (i & 1) ? -1 : 1;
    ans += 1ll * f[tot][n - i] * fac[n - i] % kMod * fl;
    ans = (ans % kMod + kMod) % kMod;
  }
  cout << ans << endl;
  return summary, 0;
}

标签:sz,颜色,kMod,int,题解,sum,cdot,模拟,20220823
来源: https://www.cnblogs.com/Soba/p/16629013.html