9月10日模拟赛题解

前言

一时爆炸一时爽,一直爆炸一直爽!

因为是教师节,所以中午就放学了,但是我们敬爱的 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 周可以:

  1. 用第 i − 1 i-1 i1 周的存货,存在货仓一周;
  2. 直接生产。
    那么方程就是:
    d p i = min ⁡ ( d p i − 1 + s , C i ) dp_i=\min(dp_{i-1}+s,C_i) dpi=min(dpi1+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,anZ+,设它们的积为 p p p,你可以找到一个 q ∈ Z + q\in\mathbb{Z^+} qZ+,使 p × q p\times q p×q 刚好为 m ! m! m! m ∈ Z + m\in\mathbb{Z^+} mZ+),求 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+=t1mi

∴ k ≤ m i t − 1 \therefore k\le\dfrac{m_i}{t-1} kt1mi

现在是知道 t , k t,k t,k 要求 m i m_i mi

移项得 m i ≥ ( t − 1 ) k m_i\ge(t-1)k mi(t1)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=(51)×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=(t1)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+txmodt 即可。

分解质因数用 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[1inai×1pmid(pmid+p2mid+p3mid+)])O(log(1inai×1pmidp1mid))

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练跑步} T3S练跑步

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值