【洛谷P1408】 互質數列
阿新 • • 發佈:2017-06-22
可能 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】 互質數列