2024.11.13 CW 模拟赛

题面 & 题解

T1

算法: 并查集

用并查集维护合并的过程, 每次合并时将能力值小的父亲定义为能力值大的父亲即可.

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
#include "iostream" using namespace std; namespace IO { template <typename T> inline void read(T &x) { x = 0; bool f = 0; char ch = getchar(); while (!isdigit(ch)) { if (ch == '-') f = 1; ch = getchar(); } while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar(); x = (f ? -x : x); return; } inline void print(int x) { static int sta[65]; int top = 0; do { sta[top++] = x % 10, x /= 10; } while (x); while (top) putchar(sta[--top] + 48); putchar('\n'); } } using namespace IO; const int N = 1e5 + 10; int n, m; int a[N]; inline void init() { read(n); for (int i = 1; i <= n; ++i) read(a[i]); read(m); return; } namespace dsu { int fa[N]; inline void init() { for (int i = 1; i <= n; ++i) fa[i] = i; return; } int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); } } inline void calculate() { dsu::init(); while (m--) { int x, y; read(x), read(y); int f1 = dsu::find(x), f2 = dsu::find(y); if (f1 == f2) print(f1); else { if (a[f1] < a[f2]) swap(f1, f2); dsu::fa[f2] = f1; print(f1); } } return; } inline void solve() { init(); calculate(); return; } int main() { #ifdef FILE_IO freopen("company.in", "r", stdin); freopen("company.out", "w", stdout); #endif solve(); return 0; }

T2

算法: 分解因数, 字符串

我们对于一个字符串, 设其最短循环节长度为 \(x\), 每次跳 \(a\) 个.

将这个字符串无限复制, 再将每 \(a\) 个字符分成一段, 找到最短的一组循环节即我们想要的.

容易得到, 该循环节的长度为 \(\frac{\rm{lcm(a,x)}}{a}\).

最后再将所有字符串的循环节长度都取一个最小公倍数即可.

\(a\) 于题目中已经给出了, 考虑如何求 \(x\).

因为不会 \(kmp\), 所以考场上乱搞了一个.

发现 \(x\) 一定为该字符串长度的因数, 所以将字符串长度 \(m\) 进行因数分解 + 排序 (后面有用).
时间复杂度 \(\mathcal{O}(n(\sqrt m + \log \log m))\).

对于每一个字符串, \(\Theta(n)\) 判断每一个因数长度的循环节是否合法, 由于排过序, 找到第一个合法的后 \(break\) 掉即可.
这样时间复杂度 \(\mathcal{O}(nm\log m)\).

总时间复杂度 \(\mathcal{O}(n(\sqrt m + m \log m + \log \log m))\), 因为 \(1 \le m \le 10^5\), 可以通过.
当然, 用 \(kmp\) 可以做到 \(\mathcal{O}(nm)\).

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
#include "iostream" #include "algorithm" #include "cmath" #include "string" using namespace std; typedef long long ll; const int N = 11; int n; int a[N], l[N]; string s[N]; inline void init() { cin >> n; for (int i = 1; i <= n; ++i) cin >> i[a]; for (int i = 1; i <= n; ++i) cin >> i[s], l[i] = s[i].size(), s[i] = " " + s[i]; return; } basic_string<int> mx, t[N]; inline void calculate() { for (int i = 1; i <= n; ++i) { int t_ = sqrt(l[i]); for (int j = 1; j <= t_; ++j) { if (!(l[i] % j)) { if (j * j < l[i]) t[i].push_back(j), t[i].push_back(l[i] / j); else t[i].push_back(j); } } sort(t[i].begin(), t[i].end()); } return; } inline bool check(int k, int r) { for (int i = 1; i <= r; ++i) { int nw = r + i; while (nw <= l[k]) { if (s[k][nw] != s[k][i]) return 0; nw += r; } } return 1; } inline ll lcm(ll x, ll y) { return x * y / __gcd(x, y); } inline void solve() { init(); calculate(); for (int i = 1; i <= n; ++i) { for (int j : t[i]) { if (check(i, j)) { mx.push_back(j); break; } } } ll ans = 1; for (int i = 0; i < mx.size(); ++i) { int x = lcm(a[i + 1], mx[i]) / a[i + 1]; ans = lcm(ans, x); } cout << ans << '\n'; return; } int main() { #ifdef FILE_IO freopen("tiger.in", "r", stdin); freopen("tiger.out", "w", stdout); #endif cin.tie(nullptr)->ios::sync_with_stdio(false); solve(); return 0; }

T3

先给两个组合数中"常用"的组合公式:

\[\sum_{i=0}^n C^x_i = C^{x+1}_{n+1} \rightarrow \sum_{i=l}^r C^x_i = C^{x+1}_{r+1} - C^{x+1}_l \]

\[\sum_{i=0}^{n} C_{x+i}^i = C^n_{x+n+1} \rightarrow \sum_{i=l}^r C^i_{x+i} = C^r_{x+r+1} - C^{l-1}_x \]

可以用数归证明 (画图模拟一下也可).

  • Subtask 2
    对于 \(40\%\) 的数据, 即求一个点到一条线的路径个数.
    \(a=b_2-d_1, b=a_2-c_1, c=c_2-a_2\), 枚举线上每一个点, 由一式推导即可得出结论:

\[\sum_{i=0}^c C^a_{a+b+i} = C^{a+1}_{a+b+c+1} - C^{a+1}_{a+b} \]

  • Subtask 3
    该部分相当于求一个点到一个矩形的路径个数.
    同 Subtask2, 再设 \(d=d_2-b_2\), 枚举矩形的每一条线, 由 Subtask2 和二式推导即可得出:

\[\sum_{i=0}^d (C_{a+b+c+1+i}^{a+1+i} - C_{a+b+i}^{a+1+i}) = \sum_{i=0}^d C_{a+b+c+1+i}^{a+i+1} - \sum_{i=0}^d C^{a+1+i}_{a+b+i} \]

\[= (C_{a+b+c+d+2}^{a+d+1}-C_{a+b+c+1}^c) - (C_{a+b+d+1}^{a+d+1} - C^a_{a+b}) \]

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
#include "iostream" using namespace std; #define int long long const int N = 1e6 + 10, mod = 998244353; int a1, b1, c1, d1; int a2, b2, c2, d2; int jc[N]; inline void init() { jc[0] = 1; for (int i = 1; i <= 1e6; ++i) jc[i] = (1ll * jc[i - 1] * i) % mod; return; } namespace Get { inline int qpow(int x, int y) { int ans = 1; while (y) { if (y & 1) ans = (ans * x) % mod; x = (x * x) % mod; y >>= 1; } return ans; } inline int inverse(int x) { return qpow(x, mod - 2); } } inline int C(int up, int dn) { return (jc[dn] * Get::inverse((jc[up] * jc[dn - up]) % mod)) % mod; } inline void in() { cin >> a1 >> b1 >> c1 >> d1; cin >> a2 >> b2 >> c2 >> d2; return; } inline void calculate() { int a = b2 - d1, b = a2 - c1, c = c2 - a2, d = d2 - b2, x = c1 - a1, y = d1 - b1; int ans; ans = ((C(a + d + y + 2, a + b + c + d + x + y + 4) - C(a + d + 1, a + b + c + d + x + 3) + mod) % mod - (C(a + d + y + 2, a + b + c + d + y + 3) - C(a + d + 1, a + b + c + d + 2) + mod) % mod - (C(a + y + 1, a + b + c + x + y + 3) - C(a, a + b + c + x + 2) + mod) % mod + mod) % mod + (C(a + y + 1, a + b + c + y + 2) - C(a, a + b + c + 1) + mod) % mod - (C(a + d + y + 2, a + b + d + x + y + 3) - C(a + d + 1, a + b + d + x + 2) + mod) % mod + (C(a + d + y + 2, a + b + d + y + 2) - C(a + d + 1, a + b + d + 1) + mod) % mod + (C(a + y + 1, a + b + x + y + 2) - C(a, a + b + x + 1) + mod) % mod - (C(a + y + 1, a + b + y + 1) - C(a, a + b) + mod) % mod; (ans += mod << 2) %= mod; cout << ans << '\n'; return; } inline void solve() { init(); int T; cin >> T; while (T--) { in(); calculate(); } return; } signed main() { #ifdef FILE_IO freopen("rectangle.in", "r", stdin); freopen("rectangle.out", "w", stdout); #endif ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); solve(); return 0; }

T4

状压板子题

只需要注意最后统计答案的时候需要遍历所有可能的合法情况, 也就是不一定所有点都走完 (不然会挂20pts).

复制代码
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
#include "iostream" #include "bitset" using namespace std; namespace IO { template <typename T> inline void read(T &x) { x = 0; bool f = 0; char ch = getchar(); while (!isdigit(ch)) { if (ch == '-') f = 1; ch = getchar(); } while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar(); x = (f ? -x : x); return; } inline void print(int x) { static short sta[65]; int top = 0; do { sta[top++] = x % 10, x /= 10; } while (x); while (top) putchar(sta[--top] + 48); putchar('\n'); } } using namespace IO; const int N = 21; int n; short w[N][N]; bitset<N> t; int nd[N], ed = 1; inline void init() { t.reset(); read(n); for (short i = 1; i <= n; ++i) for (short j = 1; j <= n; ++j) read(w[i][j]); for (short k = 1; k <= n; ++k) { int op; read(op); if (op ^ 1) { t[k] = 1; short m; read(m); for (int i = 1; i <= m; ++i) { int x; read(x); nd[k] |= (1 << x - 1); } ed |= nd[k] | (1 << k - 1); } } return; } int f[N][1 << 20]; inline void calculate() { for (int i = 1; i < (1 << n); ++i) for (short j = 1; j <= n; ++j) f[j][i] = 1e9; f[1][1] = 0; for (int i = 1; i < (1 << n); ++i) { for (short j = 1; j <= n; ++j) { if (!((i >> j - 1) & 1)) continue; for (short k = 1; k <= n; ++k) { if (!(j ^ k)) continue; if ((i >> k - 1) & 1) continue; if (t[k] and ((i & nd[k]) ^ nd[k])) continue; f[k][i ^ (1 << k - 1)] = min(f[j][i] + w[j][k], f[k][i ^ (1 << k - 1)]); } } } int ans = 1e9; for (int s = ed; s < (1 << n); ++s){ if ((s & ed) ^ ed) continue; for (int i = 1; i <= n; ++i) ans = min(ans, f[i][s] + w[i][1]); } print(ans); return; } inline void solve() { init(); calculate(); return; } int main() { #ifdef FILE_IO freopen("treasure.in", "r", stdin); freopen("treasure.out", "w", stdout); #endif solve(); return 0; }
posted @   Steven1013  阅读(5)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开