20220823 模拟赛题解
作者:互联网
T1 文件压缩
Decription
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
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
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
Solution
容易发现一个性质:如果 \(x\times y\) 和 \(y\times z\) 都是完全平方数,那么 \(x\times z\) 也是完全平方数。
那么我们就可以将这 \(n\) 个数看成 \(n\) 个不同颜色的球,相同颜色的球两两乘积就是完全平方数,那么用个并查集就可以暴力 \(O(n^2)\) 预处理出来了。
然后这个东西就转换成了:有 \(n\) 个球,一共有 \(m\) 个颜色,求相同球颜色都不相同的排列数。
这个东西直接不好做,所以考虑容斥。
不妨设 \(a_i\) 表示颜色为 \(i\) 的球的个数。
设 \(f(s)\) 表示有至少 \(s\) 对相邻的球颜色相同的方案数,那么答案就是:
我们可以将当前序列分为连续的颜色相同的若干块,设 \(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