【考试总结】2021.7.24 考试总结

前言

真就上次考试把 \(rp\) 耗光了呗。

得分:\(80+10+100+0+0=190\)

排名:\(\text{rk 22}\)(话说最终还是没进前 \(20\) 啊 wzbl)

wwh:显然,一班的三个 OIer(PS:别问我为啥只有 \(3\) 个),每次考试都是一个超常发挥,一个正常水平,还有一个跳水……

顶礼膜拜 XSC062 和 luowenzuo 小朋友!小六小四秒切 OI 班班长!!1

话说 zqw 忘写头文件结果让 mjl 给他加上了 啊这啊这

这次不想写考场的游记了,直接进入题解部分吧。


题解

T1

题意简述:给定 \(T\) 组数据,每组数据包含两个数 \(a,b\),有 \(n=\lfloor \dfrac{a!}{b!} \rfloor\)。现在对 \(n\) 进行若干次操作(称为一轮),每一次操作任意选取一个能被 \(n\) 整除且大于 \(1\) 的整数,并将 \(n\) 改为 \(n\) 除以这个数的结果,当 \(n=1\) 时一轮操作结束。求每一轮最大的操作次数。

数据范围:\(1\le T\le 10^6, 1\le b\le a\le 5\times 10^6\)


首先很显然 \(n\) 初值那个向下取整完全是来忽悠你的,这个值可以变形为:

\[\begin{equation} \label{eqn2} \begin{split} \lfloor \dfrac{a!}{b!} \rfloor &= \dfrac{1\times 2\times\ldots\times a}{1\times 2\times\ldots\times b}\\ &= (b+1)\times(b+2)\times\ldots\times(a-1)\times(a) \end{split} \end{equation} \]

也就是说这个 \(n\) 就算不加向下取整也一定是一个整数。然后我们看到题目中:

最大的操作次数。

不难想到,由于唯一分解定理,任何一个 \(n\) 的质因数分解方式是固定的。每一次都取当前 \(n\) 的一个质因数,这样每一次都只取走一个,以达到最大化操作次数的目的。

如果您还不懂的话,可以看下面这个例子。

比如 \(12\) 可以分成很多种形式:

\[\begin{equation} \label{eqn} \begin{split} 12 &= 2\times 6\\ &= 3\times 4\\ &= 2^2\times 3 \end{split} \end{equation} \]

很明显如果我们取 \(2,2,3\) 可以取到最优解。因为不管是 \(4\) 还是 \(6\),都是可以继续拆成更多的数相乘。这样我们就可以继续优化结果。

那么我们如何求出一段连续的数相乘呢?

我们再来看一个例子:

\[\begin{equation} \label{eqn3} \begin{split} 3\times4\times5\times6 &= 3\times(2^2)\times 5\times (2\times 3)\\ &= 2^3\times 3^2 \times 5 \end{split} \end{equation} \]

\[3+2+1=6 \]

\(360\)\(6\) 个质因子。因为我们又知道:\(a^b\times a^c=a^{b+c}\)(初一内容),也就是说同底数的幂相乘等于把所有指数相加后在和底数做幂运算。由此我们可以推出:答案就等于从 \(b+1\) 开始到 \(a\) 所有数的质因数之和

那如何求质因数的个数呢?

我们知道欧拉筛的时候,任何一个合数被消掉的方法是用它的最小质因数与另一个数相乘消掉。所以,我们可以用一个数组 \(num\) 来统计每一个数的质因数个数,质数初始化为 \(1\)。任何一个合数,都是把它消掉的那个的合数的质因子数量 \(+1\)

关于代码实现可以用前缀和实现 \(O(1)\) 查询。总时间复杂度 \(O(50^5+T)\)

Code

#include<cstdio>
const int MAXN=(int)5e6+5;
int n,a,b,tot,prime[MAXN],num[MAXN];
int sum[MAXN];
bool f[MAXN];
void Prime(){
	for(int i=2;i<=MAXN/2;i++){
		if(!f[i]){
			prime[++tot]=i;
			num[i]=1;
		}
		for(int j=1;j<=tot;j++){
			int d=prime[j]*i;
			if(d>=MAXN) break;
			f[d]=1;
			num[d]=num[i]+1;
			if(!(i%prime[j])) break;
		}
	}
	return;
}
int main(){
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	Prime();
	for(int i=MAXN/2+1;i<MAXN;i++) if(!f[i]) prime[++tot]=i,num[i]=1;
	for(int i=1;i<MAXN;i++) sum[i]=sum[i-1]+num[i];
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d %d",&a,&b);
		printf("%d\n",sum[a]-sum[b]);
	}
	return 0;
}

T2

题意简述:

\(n\) 个需要充电的电脑,刚开始每个电脑的电量是 \(a_1,a_2,\ldots,a_n\),每过一分钟这些电脑的电量就会减少 \(b_1,b_2,\ldots,b_n\),充电一共持续 \(k\) 分钟。gm 交给了 mjl 一个任务:给这些电脑充电。所以 mjl 需要选择一个(即每分钟只能给一个电脑充电)充电器给这些电脑充电。在充电的过程中,如果有任意一个电脑的电量低于 \(0\),那么 mjl 的任务就失败了。请帮助 mjl 选择功率(即一分钟给电脑充电的电量)尽量小并且能完成任务的功率,并输出它。如果 mjl 无论如何都无法完成任务请输出 \(-1\)

注意:每个电脑电池的容量无限大。

这叫简述(确信


不难想到二分查找答案。

关键是 \(\text{check}\) 函数怎么写呢?

1.部分分做法

类似于不守交规,因为我们每一分钟都只能给一个电脑充电,所以,只要一个电脑还能撑到下一分钟,我们就尽量把给它充电的时间往后拖。这个老师在讲贪心的时候已经详细讲过了,我这里就不赘述了~

时间复杂度 \(O(nklog一个常数)\) (算上二分,这个常数也许需要卡 qwq),但是不管怎么样会 T 掉。


满分(伪)做法

用一个优先队列存每一个电脑能坚持的时间,每一次取出最需要充电的那个充一次电,然后更新这个电脑能坚持的时间在扔回优先队列里。

注意实现的时候我们除了时间外还要存一个值:坚持完最后一分钟之后剩余的电量,方便我们计算时间。

Code

先咕着 qwq

T3

简单并查集,可是码量较大。

首先我们知道

posted @ 2021-07-24 22:25  Saiodgm  阅读(33)  评论(0编辑  收藏  举报