yzoj 2372 小B的数字 题解
题意
判断是否存在一个序列 $ b_i $ 使得 $ \prod_{i = 1}^{n} b_i | b_i^{a_i}$ 恒成立,其中 $ b_i $ 中的每个数都是2的正整数次幂。
样例输入
3 2 3 2 3 3 3 3 2 1 10
样例输出
yes yes no
数据范围
对于 100% 的数据有 $ n \leq 10^5,a_i \leq 10,t \leq 10$
解析
首先拿到这道题,考场一看就知道不是规律题就是数学公式题,事实上是的。
我们可以设 $ b_i=2^{x_i} $ 其中 $ x_i \(为正整数,\) lcm(a_1,a_2,a_3....a_n)=lcm $ , $ sum=\sum_{i = 0}^{n} x_i $。
那么我们可以将原式子化为 $ 2^{sum} | 2^{x_i * a_i} $,显然要使此式恒成立,就要满足 $ a_i * x_i \geq sum $.
此式子可以转化为 $ lcm* x_i \geq sum* \frac{lcm}{a_i} $
左右两边相加可得
$ lcm* sum \geq sum * ( \sum_{i = 1}^{n} {\frac{lcm}{a_i}} )$
即 $ lcm \geq ( \sum_{i = 1}^{n} {\frac{lcm}{a_i}} )$
两边提出 $ lcm $约去得到 $ 1 \geq ( \sum_{i = 1}^{n} {\frac{1}{a_i}} )$
那么我们可以得出最终公式就是 $ ( \sum_{i = 1}^{n} {\frac{1}{a_i}} \leq 1) $
如果我们直接同分比较,很显然会超数据范围。
对于这一题,由于涉及倒数,会产生浮点误差,我们有三种方法去处理
方法一(不推荐
在最终判断的时候设置精度进行调控
#include<bits/stdc++.h> using namespace std; const double eps=1e-6; int t,n,k; bool cheak(double a,double b){ if(a-b<=eps) return true; else return false; } int main(){ scanf("%d",&t); while(t--){ scanf("%d",&n); double sum=0; for(int i=1;i<=n;++i){ scanf("%d",&k); sum+=1.0/(double)k; } if(cheak(sum,(double)1)) printf("yes\n"); else printf("no\n"); } return 0; }
方法二(正解
我们可以观察数据,可以知道 $ a_i \leq 10 $ 我们最终得到得式子也只与 $ a_i $ 得倒数有关,所以我们可以将式子改造,左右两边乘以 $ 10! $,也就是
$ ( \sum_{i = 1}^{n} {\frac{10!}{a_i}} \leq 10!) $
于是运算便变为了整数运算,便不存在浮点误差了!(常用技巧)
#include <iostream> #include <cstdlib> #include <cstdio> using namespace std; typedef long long ll; int main(){ int t; scanf("%d",&t); while (t--){ int n; scanf("%d",&n); ll tot=0; for(int i=0;i<n;i++){ int x; scanf("%d",&x); tot+=3628800/x; } puts(tot<=3628800 ? "yes" : "no"); } return 0; }
方法三(巧妙的暴力
分析式子 $ ( \sum_{i = 1}^{n} {\frac{1}{a_i}} \leq 1) $ 我们可以发现如果 $ n > max(a_i) $ 那么这个式子必然不成立,所以我们可以把n的范围缩小到 $ max(a_i) $ 以内,那么我们通分就不会超出范围了于是便有了一个愉快的暴力
#include<bits/stdc++.h> using namespace std; int main(){ int t; scanf("%d",&t); while (t--){ int n;bool flag=1; scanf("%d",&n); long long tot=0; long long pop=1; int maxn=0; for(int i=1;i<=n;i++){ int x; scanf("%d",&x); maxn=max(maxn,x); if(x==1) flag=0; tot+=x; pop*=x; } if(!flag || n>maxn) printf("no\n"); else puts(tot<=pop ? "yes" : "no"); } return 0; }