2024.11.13 CW 模拟赛
T1
算法: 并查集
用并查集维护合并的过程, 每次合并时将能力值小的父亲定义为能力值大的父亲即可.
#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)\).
#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})
\]
#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).
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现