题目描述
给出两个数 m 1 , m 2 m1,m2 m1,m2和 n n n个数 s 1 , s 2 , . . . . , s n s_1,s_2,....,s_n s1,s2,....,sn,试在 s i s_i si中选取一个数使得 m 1 m 2 ∣ ( s i ) k m_{1}^{m_2}|(s_i)^{k} m1m2∣(si)k ( m 1 m 2 m_{1}^{m_2} m1m2整除 ( s i ) k (s_i)^{k} (si)k)中的 k k k最小. 如果找不到这样的 s i s_i si,输出“ − 1 -1 −1”.
题目解析
要求能够整除,说明 m 1 m 2 m_{1}^{m_2} m1m2的所有质因数组成的集合是 ( s i ) k (s_i)^k (si)k的所有质因数组成的集合的子集,而且两者的交集中,交集的元素出现的次数在 ( s i ) k (s_i)^k (si)k中出现得更多.
我们不妨对 m 1 m_1 m1进行质因数分解,而 m 1 m 2 m_{1}^{m_2} m1m2的质因数与 m 1 m_1 m1的质因数相同,只是每个质因数的出现个数 x ∗ m 2 x*m_2 x∗m2而已. 再对每一个输入的 s i s_i si进行质因数分解,如果 m 1 m_1 m1的所有质因数在 s i s_i si中都有体现,那么 s i s_i si就至少需要 y i = m a x ( ⌈ x ∗ m 2 s u m ⌉ ) y_i=max(\left \lceil \frac{x*m_2}{sum} \right \rceil) yi=max(⌈sumx∗m2⌉)(其中 s u m sum sum为该质因数在 s i s_i si中出现的次数, x x x为该质因数在 m 1 m_1 m1中出现的次数)次的乘方才行. 将每一次的 y i y_i yi进行比较,输出最小的即可.
代码实现
#include<bits/stdc++.h>
#define maxn 30010
#define ll long long
using namespace std;
int n,m1,m2;
int t,stac[maxn],stac_sum[maxn],p[maxn];
bool vis[maxn];
ll now,minn=0x3f3f3f3f;
void prepare(){
int op=m1;
int tot=0;
for(int i=2;i<=m1;i++){
if(!vis[i]){
p[++tot]=i;//素数数组
if(m1%i==0){
stac[++t]=i;//m1的质因子数组
while(op%i==0){
op/=i;
stac_sum[t]++;//记录质因子出现次数
}
stac_sum[t]*=m2;//质因子翻倍出现
}
}
for(int j=1;j<=tot&&p[j]*i<=m1;j++){
vis[p[j]*i]=true;
if(i%p[j]==0)break;//线性筛法
}
}
}
int main(){
scanf("%d",&n);
scanf("%d%d",&m1,&m2);
prepare();
for(int i=1;i<=n;i++){
ll maxm=0;
scanf("%lld",&now);
bool flag=true;
for(int j=1;j<=t;j++){
int s=0;
while(now%stac[j]==0){
now/=stac[j];
s++;
}
if(s==0){flag=false;break;}//特判,并没有出现所有质因子
if(stac_sum[j]<s&&maxm==0){maxm=1;continue;}
ll op=((stac_sum[j]%s==0)?(stac_sum[j]/s):(stac_sum[j]/s+1));//特判向上取整,大于等于这个数的最小整数
maxm=max(maxm,op);
}
if(!flag)continue;
minn=min(maxm,minn);//s中取最小
}
if(minn==0x3f3f3f3f)printf("-1\n");//根本没有满足条件的情况
else printf("%lld\n",minn);
return 0;
}
题后反思
1. 1. 1.对于质因子分解这种思想还不太了解,导致不看标签就没了思路. 以后要多加练习,见到类似的题想一想能不能用质因数分解,尤其是这种整除问题.
2. 2. 2.写挂了好几次这个东西: ( a b ) c = a b ∗ c ≠ a b + c (a^b)^c=a^{b*c}\neq a^{b+c} (ab)c=ab∗c̸=ab+c,说明写题时还是没有完全想清楚.