ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

20220823 模拟赛题解

2022-08-26 21:02:08  阅读:257  来源: 互联网

标签:sz 颜色 kMod int 题解 sum cdot 模拟 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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有