首页 > 代码库 > 【洛谷P1408】 互质数列

【洛谷P1408】 互质数列

这题其实比较naive……

问题是我更naive……

这题伟大的杨队长提出了一个 技术分享的dp做法……

我的做法就很naive了。

首先我们发现,如果我们对两个相邻的数进行一次操作,这个操作产生的影响最多波及的a[i+2]

(前提是我们从前向后操作)

这就为我们分类讨论提供了简化。

其次有一个很显然的结论:

设a[i],a[i+1]的gcd为x,显然我选择每次除掉x的一个质因数,至少比直接除掉x的策略要优

这个结论很显然,当且仅当两个质因数都是2的时候这两种操作的价值才相等。

或者只有一个质因数。

换而言之我们先筛出素数,然后计算每个质数产生的贡献,讨论它所能够影响的范围即可。

然后这么做发现我能被自己叉掉……

于是在这么跑完之后,再扫一次gcd数组,继续上述的讨论直接除掉gcd即可~

因为可能会剩下某些质数……

#include<bits/stdc++.h>
#define N 50005
using namespace std;
typedef long long ll;
int n,a[N],g[N],f[N],vis[N],cnt;
ll ans,sum;
int gcd(int x,int y){if(!y)return x;return gcd(y,x%y);}
inline int read(){
    int f=1,x=0;char ch;
    do{ch=getchar();if(ch==-)f=-1;}while(ch<0||ch>9);
    do{x=x*10+ch-0;ch=getchar();}while(ch>=0&&ch<=9);
    return f*x;
}
int prime[N];
void calcpri(){
    memset(vis,1,sizeof(vis));
    for(int i=2;i<=5000;i++){
        if(vis[i])prime[++cnt]=i;
        for(int j=1;j<=cnt;j++){
            int t=i*prime[j];if(t>5000)break;
            vis[t]=0;
            if(i%prime[j]==0)break;
        }
    }
}
int main(){
    n=read();for(int i=1;i<=n;i++)a[i]=read();
    for(int i=1;i<n;i++)g[i]=gcd(a[i],a[i+1]);
    calcpri();
    for(int i=1;i<=cnt;i++){
        memset(f,0,sizeof(f));
        for(int j=1;j<n;j++)while(g[j]%prime[i]==0)f[j]++,g[j]/=prime[i];
        sum=0;
        for(int j=1;j<n;j++){
            sum+=f[j];int w=f[j];f[j]=0;
            w=min(w,f[j+1]);f[j+1]-=w;
            w=min(w,f[j+2]);f[j+2]-=w;
        }
        ans+=1LL*prime[i]*sum;
    }
    for(int i=1;i<n;i++){
        if(g[i]>1){
            ans+=g[i];if(g[i+2]==g[i]&&g[i+1]==g[i])g[i+2]=1;
            if(g[i+1]==g[i])g[i+1]=1;g[i]=1;
        }
    }
    cout<<ans<<endl;
}

 

【洛谷P1408】 互质数列