Codeforces402D——Upgrading Array(数论+贪心)

传送门

可以说是道半结论题了

开始想到了大半,但是没想全

首先可以发现前缀Gcd是单调不增的

那么也就是说

越后面的数的前缀gcd是肯定小于前面的数的

同时,前面的数的前缀gcd一定是后面的前缀gcd的倍数

那么也就是说我们只能从后面往前不断除gcd

因为除完这里这里的的前缀gcd就为1了

再除后面的就没有用了

我们还可以发现

每个数的“ f ” 也就是它的贡献

是其质因子中“好的”减去“坏的”

这个很好理解吧

因为它一定是由这些质因子转移过来的

然后我们就可以发现我们可以贪心处理

从后往前枚举当前的gcd的分数

如果小于0

也就是对答案有负的贡献

那我们就除去它

这样一定是正确的

考虑如果这里没有除去

那么在更前面的地方一定还是会除去

而且中间必定会有一部分负的贡献没有被除去

所以说我们只需要从后往前处理

预处理所以前缀gcd

如果gcd贡献为负

就直接暴力除去

#include<bits/stdc++.h>
#define ll int
using namespace std;
inline int read(){
 char ch=getchar();
 int res=0;
 while(!isdigit(ch))ch=getchar();
 while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
 return res;
}
bool f;
ll ans;
int m,n,prime[100005],cnt,vis[100005],a[100005],pre[100005],zhi[1000005];
map<int,int> mp;
inline int gcd(int x,int y){
 return y==0?x:gcd(y,x%y);
}
inline void init(){
 for(int i=2;i<=100005;i++){
  if(!vis[i]){
   prime[++cnt]=i;
   for(int j=i+i;j<=100005;j+=i){
    vis[j]=true;
   }
  }
 }
}
inline int calc(int x){
 int res=0;
 for(int i=1;i<=cnt;i++){
  while(x%prime[i]==0){
   res+=(mp[prime[i]]?-1:1);
   x/=prime[i];
  }
 }
 if(x!=1){
  res+=mp[x]?-1:1;
 }
 return res;
}
signed main(){
 n=read(),m=read();
 for(int i=1;i<=n;i++)a[i]=read();
 for(int i=1;i<=m;i++)mp[read()]=1;
 init();  
 for(int i=1;i<=n;i++)pre[i]=gcd(a[i],pre[i-1]);
 for(int i=1;i<=n;i++)ans+=calc(a[i]);
 int woc=1;
 for(int i=n;i>=1;i--){
  pre[i]/=woc;
  int t=calc(pre[i]);
  if(t<0){
   ans-=t*i;
   woc*=pre[i];
  }
 }
 cout<<ans;
 return 0;
}
原文地址:https://www.cnblogs.com/stargazer-cyk/p/10366444.html