P6022 快乐水 题解
Posted bifanwen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P6022 快乐水 题解相关的知识,希望对你有一定的参考价值。
简要题意:一开始你有 (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;
}
以上是关于P6022 快乐水 题解的主要内容,如果未能解决你的问题,请参考以下文章