Loading

题解:CF2013E Prefix GCD

记值域为 \(V\),最终数组为 \(b\),令 \(pre_i=\gcd(b_1,b_2,\dots,b_i)\)

先把 \(G=\gcd(a_1,a_2,\dots,a_n)\) 提出来,\(a_i\gets \dfrac{a_i}{G}\),最后答案再乘上 \(G\)

这样 \(pre_n\) 肯定是 \(1\),我们发现假如存在 \(pre_i=pre_{i-1} \operatorname{and} pre_i\ne 1\),那么肯定不优。所以 \(pre\) 除了前 \(\log V\) 项应该都是 \(1\)

于是可以开始 dp,\(dp_{i,j}\) 表示 \(pre_i=j\)\(\sum\limits_{k=1}^i pre_k\) 的最小值。

\(f_{x,y}\)\(\exist i,\gcd(a_i,x)=y\)。那么转移方程为:

\[dp_{i,j}=\min_{f_{k,j}=1}dp_{i-1,k}+j \]

\(L=\min(n,\log V)\),答案为 \(G\times (dp_{L,1}+n-L)\),加上的 \(n-L\) 表示 \(pre\) 剩下的全是 \(1\) 的部分。

现在问题来了,如何求出 \(f\) 数组?

观察 \(\exist i,\gcd(a_i,x)=y\) 这个式子,显然 \(y\nmid x\)\(f_{x,y}=0\)

枚举 \(y\),处理出 \(T\) 为是 \(y\) 的倍数的 \(a_i\) 的集合,\(S=\{\dfrac{x}{y}|x\in T\}\),那么:

\[\exist i,\gcd(a_i,x)=y \]

相当于

\[\exist t\in S,\gcd(x,t)=1 \]

于是我们处理出 \(g(x)\) 表示 \(\sum\limits_d [(x\times d)\in S]\),那么 \(f_{x,y}=[\sum\limits_{d|(y/x)}\mu(d)g(d)\ne0]\)

这是因为 \(t\)\(x\) 的贡献为 \(\sum\limits_{d|t,d|x}\mu(d)=\sum\limits_{d|\gcd(t,x)}\mu(d)=[\gcd(t,x)=1]\)

分析时间复杂度:

\[\begin{aligned} &\sum_{y=1}^V \sum_{d=1}^{\lfloor\frac{V}{y}\rfloor}\lfloor\frac{\lfloor\frac{V}{y}\rfloor}{d}\rfloor\\ =&\sum_{y=1}^V \lfloor\frac{V}{y}\rfloor\ln\lfloor\frac{V}{y}\rfloor\\ =&O(V\ln^2V) \end{aligned} \]

Code

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<string>
#include<bitset>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=1e5+10;
const int N=1e5;
const int INF=0x3f3f3f3f;
const long long LINF=0x3f3f3f3f3f3f3f3f;
int n,m,mul,ans=0;
int a[MAXN];
bool* f[MAXN];
int cnt[MAXN],mob[MAXN];
bool vis[MAXN];
int dp[MAXN],lst[MAXN];//dp这里我加了滚动数组
basic_string <int> fac[MAXN];
inline void solve(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    mul=a[1];
    for(int i=1;i<=n;i++)
    {
        mul=__gcd(mul,a[i]);
    }
    m=0;
    for(int i=1;i<=n;i++)
    {
        a[i]/=mul;
        m=max(m,a[i]);
    }
    memset(vis,false,sizeof(bool)*(m+1));
    for(int i=1;i<=n;i++)
    {
        vis[a[i]]=true;
    }
    for(int i=1;i<=m;i++)
    {
        memset(f[i],false,sizeof(bool)*(m/i+1));
        memset(cnt,0,sizeof(int)*(m/i+1));
        for(int j=1;j<=m/i;j++)
        {
            if(vis[i*j]){
                for(int x:fac[j])
                {
                    cnt[x]++;
                }
            }
        }
        for(int j=1;j<=m/i;j++)
        {
            int sum=0;
            for(int x:fac[j])
            {
                sum+=cnt[x]*mob[x];
            }
            if(sum){
                f[i][j]=true;
            }
        }
    }
    memset(dp,0x3f,sizeof(int)*(m+1));
    for(int i=1;i<=n;i++)
    {
        dp[a[i]]=a[i];
    }
    for(int i=2;i<=n&&i<=20;i++)
    {
        memcpy(lst,dp,sizeof(int)*(m+1));
        memset(dp,0x3f,sizeof(int)*(m+1));
        for(int x=1;x<=m;x++)
        {
            for(int y:fac[x])
            {
                if(f[y][x/y]){
                    dp[y]=min(dp[y],lst[x]+y);
                }
            }
        }
    }
    if(n<=20){
        printf("%lld\n",1ll*mul*dp[1]);
    }
    else{
        printf("%lld\n",1ll*mul*(dp[1]+n-20));
    }
}
inline void init(){
    for(int i=1;i<=N;i++)
    {
        f[i]=new bool[N/i+1];
        for(int j=i;j<=N;j+=i)
        {
            fac[j].push_back(i);
        }
    }
    mob[1]=1;
    for(int i=1;i<=N;i++)
    {
        for(int j=i+i;j<=N;j+=i)
        {
            mob[j]-=mob[i];
        }
    }
}
signed main(){
    init();
    int t;
    scanf("%d",&t);
    while(t--)
    {
        solve();
    }
    return 0;
}
posted @ 2025-02-09 07:14  Mathew_Miao  阅读(16)  评论(0)    收藏  举报