1. 程式人生 > >【洛谷P1408】 互質數列

【洛谷P1408】 互質數列

可能 ans 簡化 tro 出了 its mem ive oid

這題其實比較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】 互質數列