P6022 快乐水 题解

CSDN同步

原题链接

简要题意:一开始你有 \(n\) 瓶快乐水,每拥有 \(1\) 瓶快乐水就可以附带 \(n\) 个物件,第 \(i\) 个物件有了 \(a_i\) 个就可以 再获得 \(1\) 瓶快乐水。不允许借代 / 赊账,求最多得到的快乐水的瓶数,如果是无限多则输出 \(\text{Inf}\).

这是洛谷一道月赛题的 \(T1\).

首先,本人赛时并没有想很多,当时只想着: \(T1\) 应该是水模拟吧,可是当时仔细想:\(m = 5\) 万一死循环陷入环内,怎么办?万一 \(\text{Inf}\) 情况判不出来,怎么办?万一被卡出 \(\text{TLE}\) 怎么办?

但是我不心慌,决定一一解决。

实则真正意义上的模拟,是那种 一眼看起来就是模拟水题,然后直接乱发过掉 的,而不是 通过一定量思考发现可以模拟解决再过掉 的,本题被评为橙题是不应该的,不应该。

死循环陷入环内这一问题是最棘手的。但是仔细想:每次 你得到的快乐水瓶数只会越来越多,越来越多,从来不存在少的情况。如何判断结束呢?一个快乐水也衍生不出来就可以结束了。

那么如何判断无限情况呢?很显然,无限 当且仅当所有 \(a_i\) 相等 且 \(a_i \leq n\). 为什么呢?因为,如果所有 \(a_i\) 都相等并且第一次可以衍生的话,首先 之后每次操作 \(a_i\) 都相等,然后每次衍生出的 \(m\) 个都会再产生,再产生 \(\cdots \cdots\) 所以是无限的,具体不懂可以看看样例。

这一切都解决之后,我们想一想 \(\text{TLE}\) 问题该怎么办?

可是你想想,真会 \(\text{TLE}\) 么?

我们需要构造一组数据使得借和还的次数最多!

这里美妙优秀的 \(\text{WYXkk}\) 出题人给出了一组美妙无比的 \(\text{hack}\) 数据:

10000 5
2 3 7 43 1807

其构造方法在于,\(a_i = \Big( \prod_{j=1}^{i-1} a_j \Big) + 1\),并且 \(a_i = 2\).

经过实测,这个数据会达到 \(2.8 \times 10^7\) 次!(答案达到 \(3 \times 10^{10}\),需要部分开启 \(\text{long long}\)

当然如果 \(m=6\) 模拟就无力解决啦。

时间复杂度:\(O(\text{wys})\).(大概很优秀吧)

实际得分:\(100pts\).

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
	int x=0;while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();return x*f;}

int n,m; long long ans=0;
int a[6],b[6],c[6];

long long l;
bool x=0; //无限情况
inline void check(){
	for(int i=1;i<=m;i++)
		if(b[i]>=a[i]) l+=b[i]/a[i],b[i]%=a[i];
} //当前瓶数为 b , a 是初始数组 , l 记录多出来的瓶数

int main(){
	n=read(),m=read();
	for(int i=1;i<=m;i++) a[i]=read(),b[i]=n;
	ans+=n;
	while(1) {
		for(int i=1;i<=m;i++) c[i]=b[i];
		l=0; check();
		if(!l) break;
		ans+=l;
		for(int i=1;i<=m;i++) b[i]+=l; //记录答案,重新衍生
		bool f=0;
		for(int i=1;i<=m;i++) if(c[i]-b[i]) {f=1;break;} //做标记
		if(!f) {x=1;break;} //如果 c[i] = b[i] 则说明一次之后根本没变 , 是无限情况
	}
	if(x) printf("Inf"); else
	printf("%lld\n",ans);
	return 0;
}
posted @ 2020-04-18 19:57  bifanwen  阅读(265)  评论(0编辑  收藏  举报