CW 11.13 模拟赛 个人记录
T1
算法
暴力
暴力思路是显然的, 观察到并查集可以 \(\mathcal{O}(n \log n)\) 的维护题目中求的信息
对于 \(50\%\) 的数据显然可以通过
耗时 \(10 \rm{min}\) , 正常
正解
暴力疑似就是正解 ?????
代码
这个题只要挂了我就趋势, 但是看这样子来说应该是 \(T1\) 放了简单题
不挂的话可以保 \(100 \rm{pts}\) , 应该是稳的
#include <bits/stdc++.h>
#define int long long
const int MAXN = 1e5 + 20;
int n;
int Val[MAXN];
int m;
int u, v;
struct Union_Set_Struct
{
int fa[MAXN];
void fa_init()
{
for (int i = 1; i <= n; i++)
fa[i] = i;
}
int find(int x) { return fa[x] = (x == fa[x]) ? x : find(fa[x]); }
void merge(int u, int v)
{
int Led_u = find(u), Led_v = find(v);
if (Led_u == Led_v)
{
printf("%lld\n", Led_v);
return;
}
if (Val[Led_u] > Val[Led_v])
fa[Led_v] = Led_u, printf("%lld\n", Led_u);
else
fa[Led_u] = Led_v, printf("%lld\n", Led_v);
}
} Union_Set;
signed main()
{
freopen("company.in", "r", stdin);
freopen("company.out", "w", stdout);
scanf("%lld", &n);
for (int i = 1; i <= n; i++)
scanf("%lld", &Val[i]);
scanf("%lld", &m);
Union_Set.fa_init();
while (m--)
{
scanf("%lld %lld", &u, &v);
Union_Set.merge(u, v);
}
return 0;
}
总结
啊?
T2
前言
别死磕, 别畏难, 别管别人!!!
时间分配
先打暴力
做好对拍
根据 \(\rm{Subtask}\) 推做法
不要慌
记得检查
做好笔记 (今天的打法非常好)
不是这题怎么给 \(70 \rm{pts}\) 的部分分啊???
希望做法是真的 ( \(\rm{WC}\) 对了一下大概是好的 )
算法
\(\rm{Subtask}\) 1
模拟王启动
对于 \(30\%\) 的数据非常好打
\(\rm{Subtask}\) 2
\(a_i = 1\) , 标准找 \(\rm{lcm}\) , 提示了一下做法
\(\rm{Subtask}\) 3
\(n = 1\) , 只有一个玻璃框 , 也是提示
正解
讲真 \(\rm{Subtask} \text{ } 2, 3\) 推出来就有了
手玩样例, 有一道之前的题倒是很像
观察到问题可以转化为求一个字符串的最短循环节问题,
这是 KMP 的一个典型应用, 可以用 \(\mathcal{O}(n)\) 的时间复杂度解决
考虑流程如下
读入 \(\to\) 处理每个字符串的最短循环节 \(\to\) 问题转化为求
\(\text{最短循环节长度 } \mid a_i \times k \text{ } \rm{s.t} \text{ } k \to min\)
现在我们已知每个字符串最少要 \(k_i\) 次才能回到原先的模样
对于所有 \(k_i\) , 求其最小公倍数即可
代码
讲个笑话, 我在考场上写博客... (是的, 这些思路都是记录考场口胡的)
先看一下 KMP 求最短循环节的代码还会不会写
在这里提前想组数据, 造福几十分钟后的我
in:
3
5 11 7
abcabcabcabc
fhdsijgfhdsijg
yangxiaobao
out:
231
这应该挺平凡吧, 不会再被诈骗了
这个时候要赌一把了 开考 \(2 h\) 想保住 \(170 \rm{pts}\) 还需要打数据点分治
赌一手后面分更多 + 我代码不挂
赌输了可能要爆零
关于 KMP
class Shortest_Loop_Class
{
private:
int Next[MAXLEN];
void getNext(int p, int plen)
{
memset(Next, 0, sizeof(Next));
Next[0] = 0, Next[1] = 0;
for (int i = 1; i < plen; i++) {
int j = Next[i];
while (j && str[p][i] != str[p][j])
j = Next[j];
if (str[p][i] == str[p][j]) Next[i + 1] = j + 1;
else Next[i + 1] = 0;
}
}
public:
int Len[MAXN];
void solve()
{
for (int i = 1; i <= n; i++) {
int NowLen = str[i].length();
getNext(i, NowLen);
Len[i] = (NowLen % (NowLen - Next[NowLen]) == 0) ? (NowLen - Next[NowLen]) : NowLen;
}
return;
}
} Shortest_Loop;
\(20 \rm{min}\) 过去了, 我 KMP 挂了, 寄
记得开 \(\rm{long} \text{ } \rm{long}\)
总的代码
#include <bits/stdc++.h>
#define int long long
const int MAXN = 15;
const int MAXLEN = 1e5 + 20; // 641
#define lcm(a, b) a / std::__gcd(a, b) * b
int n;
int a[MAXN];
std::string str[MAXN];
class Shortest_Loop_Class
{
private:
int Next[MAXLEN];
void getNext(int p, int plen)
{
memset(Next, 0, sizeof(Next));
Next[0] = 0, Next[1] = 0;
for (int i = 1; i < plen; i++) {
int j = Next[i];
while (j && str[p][i] != str[p][j])
j = Next[j];
if (str[p][i] == str[p][j]) Next[i + 1] = j + 1;
else Next[i + 1] = 0;
}
}
public:
int Len[MAXN];
void solve()
{
for (int i = 1; i <= n; i++) {
int NowLen = str[i].length();
getNext(i, NowLen);
Len[i] = (NowLen % (NowLen - Next[NowLen]) == 0) ? (NowLen - Next[NowLen]) : NowLen;
}
return;
}
} Shortest_Loop;
int k[MAXN];
signed main()
{
freopen("tiger.in", "r", stdin);
freopen("tiger.out", "w", stdout);
scanf("%lld", &n);
for(int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
for (int i = 1; i <= n; i++)
std::cin >> str[i];
Shortest_Loop.solve();
for (int i = 1; i <= n; i++)
k[i] = lcm(Shortest_Loop.Len[i], a[i]) / a[i];
int Ans = 1;
for (int i = 1; i <= n; i++)
Ans = lcm(Ans, k[i]);
printf("%lld", Ans);
return 0;
}
总结
多见些题总是好的
前言里的东西好好记住
\(\rm{Subtask}\) 推正解大法好
T3
前言
考试还有 \(50 \rm{min}\), 只能拼点暴力上去
已经结束咧
算法
讲真看着好像可做啊
这套题还挺好的, 遗憾
感觉 \(\rm{lhs}\) 又要 \(\rm{AK}\) 了 , 只剩下一个小时了
暴力
选点的方式是一定的, 枚举两两之间的点, 再简单的计算一下路径种类数
\(20\%\) 的 \(\rm{Subtask}\) 相当于提示你算基础的
对于点 \((x_1, y_1) \to (x_2, y_2)\) 的路径数计算
有横向移动次数 \(a = \lvert{x_2 - x_1\rvert}\) , 纵向移动次数 \(b = \lvert{y_2 - y_1\rvert}\)
穿插加入即可
种类数为 $Ans = \frac{(a + b)!}{a! \times b!} $
观察到直到 \(60\%\) 的数据都有起始点一定, 但是终点的数量级导致我应该只能拼上去一个 \(40 \rm{pts}\)
代码
#include <bits/stdc++.h>
#define int long long
const int MOD = 998244353;
int T;
int X1a, Y1a, X1b, Y1b, X2a, Y2a, X2b, Y2b;
/*起始点恒定, 终点为一条线*/
class Subtask_AB_Class
{
private:
int Mul(int x)
{
int Ans = 1;
for (int i = x; i >= 1; i--)
Ans *= i, Ans %= MOD;
return Ans;
}
int quickpow(int x, int p)
{
int base = x % MOD;
int Ans = 1;
while(p) {
if(p & 1) Ans %= MOD, Ans *= base, Ans %= MOD;
base = base * base % MOD;
p >>= 1;
}
return Ans % MOD;
}
public:
int Ans = 0;
void solve()
{
Ans = 0;
for (int i = X2a; i <= X2b; i++)
{
int a = abs(i - X1a), b = abs(Y2a - Y1b);
Ans += Mul(a + b) / (Mul(a) * Mul(b));
Ans %= MOD;
}
printf("%lld\n", Ans);
}
} Subtask_AB;
signed main()
{
freopen("rectangle.in", "r", stdin);
freopen("rectangle.out", "w", stdout);
scanf("%lld", &T);
while(T--) {
scanf("%lld %lld %lld %lld %lld %lld %lld %lld", &X1a, &Y1a, &X1b, &Y1b, &X2a, &Y2a, &X2b, &Y2b);
Subtask_AB.solve();
}
return 0;
}
正解
只能考完之后补了
总结
妈妈生的
T4
前言
打到这里只有 \(13 \rm{min}\) 了
拼不动了
算法
暴力
只有 \(1\) 号地点放置宝箱 / 钥匙的 \(40 \rm{pts}\) 应该能做
本质上就是求遍历所有点在绕回来的最短路径
没打出来呜呜呜呜
正解
回来补
总结
阿米诺斯
这次部分分挺足的, 寄!
盲猜倒数