Luogu P5856 【「SWTR-03」Game】
Description
Solution
读完题面之后我们首先可以想到要进行质因数分解。
因为每次只能除以\(prime^z\)也就是说每次我们只能消除某一个质因子多出来的部分,所以对于每个质因子可以分开考虑。
消去某个质因子多出来的部分只需要把这个质因子所有出现过的在每个数中的次数都记录下来,然后将一些数除以\(prime^z\)就相当于是从这些数中任意选取一些统一减去\(z\)。
那么这些数减到什么时候合法呢?因为最后所有这\(n\)个数都必须相等,所以每个质因子在每个数中的次数最多是所有这\(n\)个数的\(gcd\)中该质因子出现的次数。
所以现在问题发生了转换,变成有一些数开始时从大到小递增,这些数只能是\([1,20]\)中的数,要进行尽可能少的操作将所有数变成一样的且次数小于该质因子在\(gcd\)中出现的次数。
接着可以发现,对于不同的质因子减数的效果是一样的,所以我们可以预处理出不同次数的最少次数,即用\(dp[s]\)表示状态为\(s\)时最少操作次数。
因为不一定要消到所有的质因子次数都为\(0\),所以预处理\(gcd\)中每个质因子出现的次数,这样只需要枚举最后每个质因数最后的次数的可行范围,即将\(s\)右移即可求出答案。
Code
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n,a[724000][21],p[1000050],cnt,prime[1000050],ans,maxx,x[100050],anstmp,dp[2000050],gcdd,t[724000];
int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
void xxs()
{
p[0]=p[1]=1;
for(int i=2;i<=1000000;i++)
{
if(!p[i])prime[++cnt]=i;
for(int j=1;j<=cnt && prime[j]*i<=1000000;j++)
{
p[prime[j]*i]=1;
if(i%prime[j]==0)break;
}
}
}
void make(int x)
{
for(int i=1;i<=cnt && prime[i]<=x;i++)
{
int tmp=0;
while(x%prime[i]==0)x/=prime[i],tmp++;
if(tmp>0)a[i][tmp]=1;
}
}
void print(int x)
{
for(int i=20;i>=1;i--)
if((1<<(i-1)&x)==1<<(i-1))cout<<1;
else cout<<0;
cout<<endl;
}
int main()
{
xxs();
scanf("%d",&n);
scanf("%d",&x[1]);
maxx=gcdd=x[1];
for(int i=2;i<=n;i++)scanf("%d",&x[i]),maxx=max(maxx,x[i]),gcdd=gcd(gcdd,x[i]);
for(int i=1;i<=cnt && prime[i]<=gcdd;i++)
{
int tmp=0;
while(gcdd%prime[i]==0)gcdd/=prime[i],tmp++;
if(tmp>0)t[i]=tmp;
}
for(int i=1;i<=n;i++)make(x[i]);
for(int i=1;i<=1000500;i++)dp[i]=0x7f7f7f7f;
dp[0]=0;
for(int zt=1;zt<(1LL<<20);zt++)
{
int maxx;
for(int i=20;i>=1;i--)if((zt>>i-1)&1==1){maxx=i;break;}
for(int q=1;q<=maxx;q++)dp[zt]=min(dp[zt],dp[(zt&((1<<q-1)-1))|(zt>>q)]+1);
}
for(int i=1;i<=cnt && prime[i]<=maxx;i++)
{
int begin=0,minn,minnn;
for(int j=1;j<=20;j++)if(a[i][j])begin|=(1<<(j-1));
minnn=dp[begin];
for(int j=1;j<=t[i];j++)minnn=min(minnn,dp[begin>>j]);
ans+=minnn;
}
printf("%d",ans);
return 0;
}