前言
一时爆炸一时爽,一直爆炸一直爽!
因为是教师节,所以中午就放学了,但是我们敬爱的 tc 还给我们准备了一场模拟赛!
T1 \text{T1} T1:签到。直接 D P \rm DP DP。评分 10 \color{Red}10 10。
T2 \text{T2} T2:签到 2 2 2。分解一下质因数再取个 max \max max 就没了(我竟然搞出了 O ( n + ∑ a i ) \mathcal{O}(n+\sum \sqrt{a_i}) O(n+∑ai) 比题解解快的方法)。评分 30 \color{Red}30 30。
update:
算法被卡了。
T3 \text{T3} T3:签到 3 3 3。在 S P F A \rm SPFA SPFA 的模板上进行更改即可。评分 30 \color{Red}{30} 30.
T4 \text{T4} T4: D P + \rm DP+ DP+ 容斥。评分 60 \color{Red}60 60。
然后出现了玄学错误,导致从 rk2 掉到 rk5。
T3 \text{T3} T3 的读入我是这样写的:
for (int i = 1; i <= n; i++)
{
getchar();
for (int j = 1; j <= m; j++)
{
char c = getchar();
这个在本地测都没问题,但评测机和 LuoguIDE \text{LuoguIDE} LuoguIDE 都输出了另一个答案。
后来 大桥封印术! 告诉我千万不要用 g e t c h a r \rm getchar getchar,于是改成了:
for (int i = 1; i <= n; i++)
{
char c;
for (int j = 1; j <= m; j++)
{
cin >> c;
然后就有 70 70 70 分了。
正文
T1奶酪工厂 \text{T1奶酪工厂} T1奶酪工厂
Description \text{Description} Description
有一个奶酪工厂,在 N N N 周里,第 i i i 周,生产一单位奶酪需要 C i C_i Ci 便士。有一个货栈,保存一单位奶酪,每周需要 S S S 便士。货栈可以存无限量的奶酪,而且保证它们不变质。
每周有一个订单,在第 i i i 周需要交付 Y i Y_i Yi 单位的奶酪,第 i i i 周生产的奶酪以及之前的存货都可以交付。
请计算完成这 N N N 周的订单花的最小钱数。
Solution \text{Solution} Solution
发现第 i i i 周的订单可以选择用第 i i i 周生产的奶酪或之前的存货交付,这是一个递推的过程。
我们设 d p i dp_i dpi 表示第 i i i 周生产一单位奶酪需要的最少钱数,那么第 i i i 周可以:
- 用第 i − 1 i-1 i−1 周的存货,存在货仓一周;
- 直接生产。
那么方程就是:
d p i = min ( d p i − 1 + s , C i ) dp_i=\min(dp_{i-1}+s,C_i) dpi=min(dpi−1+s,Ci)
但是注意第一周只能直接生产,所以我们可以初始化 d p 0 = I N F dp_0=INF dp0=INF。
时间复杂度为 O ( n ) \mathcal{O}(n) O(n)。
Code \text{Code} Code
#include <iostream>
#include <cstdio>
#define int long long
using namespace std;
const int MAXN = 10005;
int c[MAXN], y[MAXN], dp[MAXN];
signed main()
{
int n, s, ans = 0;
scanf("%lld%lld", &n, &s);
dp[0] = 0x3f3f3f3f3f3f3f3fll;
for (int i = 1; i <= n; i++)
{
scanf("%lld%lld", c + i, y + i);
dp[i] = min(dp[i - 1] + s, c[i]);
ans += dp[i] * y[i];
}
printf("%lld\n", ans);
return 0;
}
T2阶乘 \text{T2阶乘} T2阶乘
Description \text{Description} Description
有 n n n 个数 a 1 , a 2 … , a n ∈ Z + a_1,a_2\dots,a_n\in\mathbb{Z^+} a1,a2…,an∈Z+,设它们的积为 p p p,你可以找到一个 q ∈ Z + q\in\mathbb{Z^+} q∈Z+,使 p × q p\times q p×q 刚好为 m ! m! m!( m ∈ Z + m\in\mathbb{Z^+} m∈Z+),求 m m m 的最小值。
Solution \text{Solution} Solution
题解用了玄学二分,复杂度比我的方法多了个 log \log log,然而因为 n n n 很小所以显不出优势。
和 大桥封印术! 交换思路后发现两人凑一凑就能凑出答案。
先在读入时将 p p p 分解质因数,假设长这样子: p = 2 a × 3 b × 5 c × ⋯ p=2^a\times3^b\times5^c\times\cdots p=2a×3b×5c×⋯,设 m 1 m_1 m1 为满足 m ! m! m! 中含 2 a 2^a 2a 的 m m m 的最小值, m 2 m_2 m2 为满足 m ! m! m! 中含 3 b 3^b 3b 的 m m m 的最小值, m 3 m_3 m3 为满足 m ! m! m! 中含 5 c 5^c 5c 的 m m m 的最小值……
那么显然当 m m m 取 max { m i } \max\{m_i\} max{mi} 时, m m m 是同时含所有的最小值。
于是问题转化为如何求 m i m_i mi。
现在要让 m i ! m_i! mi! 中含 t k t^k tk,求 m i m_i mi 的最小值,我们来推推柿子:
k = ⌊ m i t ⌋ + ⌊ m i t 2 ⌋ + ⌊ m i t 3 ⌋ + ⋯ ≤ m i t + m i t 2 + m i t 3 + ⋯ = m i t − 1 \begin{aligned}k&=\left\lfloor \dfrac{m_i}{t}\right\rfloor+\left\lfloor \dfrac{m_i}{t^2}\right\rfloor+\left\lfloor \dfrac{m_i}{t^3}\right\rfloor+\cdots\\&\le\dfrac{m_i}{t}+\dfrac{m_i}{t^2}+\dfrac{m_i}{t^3}+\cdots\\&=\dfrac{m_i}{t-1}\end{aligned} k=⌊tmi⌋+⌊t2mi⌋+⌊t3mi⌋+⋯≤tmi+t2mi+t3mi+⋯=t−1mi
∴ k ≤ m i t − 1 \therefore k\le\dfrac{m_i}{t-1} ∴k≤t−1mi
现在是知道 t , k t,k t,k 要求 m i m_i mi。
移项得 m i ≥ ( t − 1 ) k m_i\ge(t-1)k mi≥(t−1)k
但这个可能有 bug,以 t = 5 , k = 3 t=5,k=3 t=5,k=3 为例:此时算出来的 m i = ( 5 − 1 ) × 3 = 12 m_i=(5-1)\times3=12 mi=(5−1)×3=12。
这个显然是错的, 12 12 12 本来就不含质因数 5 5 5,怎么可能是最小的?
可以发现 12 ! 12! 12! 只含 5 2 5^2 52,而 15 ! 15! 15! 含 5 3 5^3 53,才是正确的 m i m_i mi。
我们设 x = ( t − 1 ) k x=(t-1)k x=(t−1)k,那么 m i m_i mi 应该是不小于 x x x 的数中最小的 t t t 的倍数,我们直接用 x + t − x m o d t x+t-x\mod t x+t−xmodt 即可。
分解质因数用 O ( n ) + ∑ a i \mathcal{O}(n)+\sum\sqrt{a_i} O(n)+∑ai,求 m i m_i mi 用 o ( n ) \mathcal{o}(n) o(n),总时间复杂度为 O ( n + ∑ a i ) \mathcal{O}(n+\sum\sqrt{a_i}) O(n+∑ai)。
Code \text{Code} Code
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 1e5 + 5;
int tot;
int a[MAXN], p[MAXN], cnt[MAXN], x[MAXN];
bool vis[MAXN];
void pre() //预处理
{
for (int i = 2; i <= MAXN; i++)
{
if (!vis[i])
{
p[++tot] = i;
x[i] = i;
}
for (int j = 1; j <= tot && i * p[j] <= MAXN; j++)
{
vis[i * p[j]] = true;
x[i * p[j]] = p[j];
if (i % p[j] == 0)
{
break;
}
}
}
}
void dyy(int n) //分解质因数
{
while (n != 1)
{
cnt[x[n]]++;
n /= x[n];
}
}
int main()
{
int n, maxx = 0, ans = 0;
scanf("%d", &n);
pre();
for (int i = 1; i <= n; i++)
{
scanf("%d", a + i);
maxx = max(maxx, a[i]);
dyy(a[i]);
}
for (int i = 2; i <= maxx; i++)
{
if (cnt[i])
{
int k = (i - 1) * cnt[i];
ans = max(ans, k + i - k % i);
}
}
printf("%d\n", ans);
return 0;
}
update:
做法被 hack 了,取到不小于
x
x
x 的数中最小的
t
t
t 的倍数还是有可能不够,例如:
Input
5
2 2 2 2 2
Output
6
Answer
8
Solution \text{Solution} Solution
所以还是得用二分。
上界为 ∑ a i \sum a_i ∑ai,二分 m m m, check ( m i d ) \operatorname{check}(mid) check(mid) 判断每个质因数的个数够不够。
时间复杂度为 O ( log [ ∑ 1 ≤ i ≤ n a i × ∑ 1 ≤ 质 数 p ≤ m i d ( ⌊ m i d p ⌋ + ⌊ m i d p 2 ⌋ + ⌊ m i d p 3 ⌋ + ⋯ ) ] ) ≈ O ( log ( ∑ 1 ≤ i ≤ n a i × ∑ 1 ≤ 质 数 p ≤ m i d ⌊ m i d p − 1 ⌋ ) ) \mathcal{O}(\log[\sum_{1\le i\le n} a_i\times \sum_{1\le质数p\le mid}(\left\lfloor\dfrac{mid}{p}\right\rfloor+\left\lfloor\dfrac{mid}{p^2}\right\rfloor+\left\lfloor\dfrac{mid}{p^3}\right\rfloor+\cdots)])\approx\mathcal{O}(\log(\sum_{1\le i\le n} a_i\times\sum_{1\le质数p\le mid}\left\lfloor\dfrac{mid}{p-1}\right\rfloor)) O(log[∑1≤i≤nai×∑1≤质数p≤mid(⌊pmid⌋+⌊p2mid⌋+⌊p3mid⌋+⋯)])≈O(log(∑1≤i≤nai×∑1≤质数p≤mid⌊p−1mid⌋))。
Code \text{Code} Code
#include <iostream>
#include <cstdio>
#define int long long
using namespace std;
const int MAXN = 1e5 + 5;
int tot;
int a[MAXN], p[MAXN], cnt[MAXN];
bool vis[MAXN];
void pre(int n)
{
for (int i = 2; i <= n; i++)
{
if (!vis[i])
{
p[++tot] = i;
}
for (int j = 1; j <= tot && i * p[j] <= n; j++)
{
vis[i * p[j]] = true;
if (i % p[j] == 0)
{
break;
}
}
}
}
void dyy(int n)
{
for (int i = 1; p[i] * p[i] <= n; i++)
{
while (n % p[i] == 0)
{
n /= p[i];
cnt[p[i]]++;
}
}
if (n != 1)
{
cnt[n]++;
}
}
bool check(int n)
{
for (int i = 1; i <= tot; i++)
{
int t = n, res = 0;
while (t > 1)
{
t /= p[i];
res += t;
}
if (res < cnt[p[i]])
{
return false;
}
}
return true;
}
signed main()
{
int n, sum = 0;
scanf("%lld", &n);
pre(n);
for (int i = 1; i <= n; i++)
{
scanf("%lld", a + i);
dyy(a[i]);
sum += a[i];
}
int l = 1, r = sum, ans;
while (l <= r)
{
int mid = (l + r) >> 1;
if (check(mid))
{
ans = mid;
r = mid - 1;
}
else
{
l = mid + 1;
}
}
printf("%lld\n", ans);
return 0;
}
T3小S练跑步 \text{T3小S练跑步} T3小S练跑步
Description \text{Description} Description
有一张地图,每一个位置上都写了到达该位置后不能往哪一个方向移动。有
5
5
5 种表示的符号:U
代表不能向上移动,D
代表不能向下移动,L
代表不能向左移动,R
代表不能向右移动,S
代表该位置有障碍物,到达那里后无法继续训练。整个公园共有
n
n
n 行
m
m
m 列,要从第
1
1
1 行第
1
1
1 列出发,到第
n
n
n 行第
m
m
m 列结束练习。
求从起点到终点途中最少要改变几次方向。
Solution \text{Solution} Solution
直接用 S P F A \rm SPFA SPFA 搜,用 l s t lst lst 记录当前方向,注意更改了松弛条件, d i s dis dis 数组也需要开成三维。
时间复杂度为 O ( n m ) \mathcal{O}(nm) O(nm)。
Code \text{Code} Code
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
const int MAXN = 505;
int n, m;
int mp[MAXN][MAXN], fx[5][2] = {{0, 0}, {-1, 0}, {1, 0}, {0, -1}, {0, 1}}, dis[MAXN][MAXN][5];
bool vis[MAXN][MAXN][5];
struct que
{
int x, y, lst;
};
void spfa()
{
memset(dis, 0x3f, sizeof(dis));
queue<que> q;
for (int i = 1; i <= 4; i++)
{
dis[1][1][i] = 0;
q.push(que{1, 1, i});
}
while (!q.empty())
{
int x = q.front().x, y = q.front().y, lst = q.front().lst;
q.pop();
vis[x][y][lst] = false;
if (mp[x][y] == -1)
{
continue;
}
for (int i = 1; i <= 4; i++)
{
int tx = x + fx[i][0], ty = y + fx[i][1];
if (tx < 1 || ty < 1 || tx > n || ty > m || mp[x][y] == i)
{
continue;
}
if (dis[x][y][lst] + (lst != i) < dis[tx][ty][i])
{
dis[tx][ty][i] = dis[x][y][lst] + (lst != i);
if (!vis[tx][ty][i])
{
vis[tx][ty][i] = true;
q.push(que{tx, ty, i});
}
}
}
}
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
char c;
for (int j = 1; j <= m; j++)
{
cin >> c;
if (c == 'S')
{
mp[i][j] = -1;
}
else if (c == 'U')
{
mp[i][j] = 1;
}
else if (c == 'D')
{
mp[i][j] = 2;
}
else if (c == 'L')
{
mp[i][j] = 3;
}
else
{
mp[i][j] = 4;
}
}
}
spfa();
int ans = 0x3f3f3f3f;
for (int i = 1; i <= 4; i++)
{
ans = min(ans, dis[n][m][i]);
}
if (ans == 0x3f3f3f3f)
{
puts("No Solution");
}
else
{
printf("%d\n", ans);
}
return 0;
}
T4轨道 \text{T4轨道} T4轨道
Description \text{Description} Description
对于公式
v
=
a
1
×
a
2
×
⋯
×
a
n
k
v=\dfrac{a_1\times a_2\times\cdots\times a_n}{k}
v=ka1×a2×⋯×an
给出
n
,
m
,
k
n,m,k
n,m,k,求这
n
n
n 个正整数
a
1
,
a
2
,
…
,
a
3
a_1,a_2,\dots, a_3
a1,a2,…,a3 在都不大于
m
m
m 的情况下有多少种选择方式,使得
v
v
v 为与
k
k
k 互质的正整数,答案对
10007
10007
10007 取模。
Solution \text{Solution} Solution
Code \text{Code} Code