P6022 快乐水 题解

CSDN同步

原题链接

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

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

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

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

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

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

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

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

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

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

这里美妙优秀的 ( ext{WYXkk}) 出题人给出了一组美妙无比的 ( ext{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 imes 10^7) 次!(答案达到 (3 imes 10^{10}),需要部分开启 ( ext{long long})

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

时间复杂度:(O( ext{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
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/bifanwen/p/12727720.html