题目传送门
题意:
n个数的序列a 和 m个数的序列b。
序列b全是素数。
出现在序列b中的数全是坏素数。不出现在序列b中的数全是好素数。
有一种操作:选择任意一段区间[1,r],即某一段前缀,设d是这段区间的gcd,让这段区间的数全部除d。
有一个函数 :
。
, p是s的最小质因子,且p是好素数。
, p是s的最小质因子,且p是坏素数。
你可以进行任意次操作,也可以不操作。
计算 的最大值。
题解:
分析函数 可以知道 等于x的好素因子个数 减去 x的坏素因子个数。
扫描二维码关注公众号,回复:
9153208 查看本文章
还需要知道:前缀的gcd是不增的。即设 1 <= i < j <= n,假设你先进行[1,i]操作,在进行[1,j]操作,那么[1,j]操作将毫无意义。但是反过来就有可能更优。
因此你倒着做,即从下标大到下标小的顺序遍历a序列。
设你遍历到 ,假如[1,i]进行操作更优,那么就进行这个操作,否则不进行这个操作。
感受:
这题看着真唬人,我一看就不会,就去看题解去了。
分析下来不难,但是要发现一些重要的条件。
代码:
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
typedef pair<int , int> pii ;
const int maxn = 5005 ;
map<int , bool> used ;
int n , m ;
int a[maxn] , b[maxn] , d[maxn] ;
int suf[maxn] ;
int cnt = 0 ;
bool vis[100005] ;
int prime[100005] ;
void get_prime()
{
memset(vis , 0 , sizeof(vis)) ;
vis[1] = 1 ;
for(int i = 2 ; i <= 1e5 ; i ++)
{
if(!vis[i])
prime[++ cnt] = i ;
for(int j = 1 ; j <= cnt && i * prime[j] <= 1e5 ; j ++)
{
vis[i * prime[j]] = 1 ;
if(i % prime[j] == 0) break ;
}
}
}
int cal(int x)
{
int ans = 0 ;
for(int i = 1 ; i <= cnt && prime[i] * prime[i] <= x ; i ++)
{
while(x % prime[i] == 0)
{
x /= prime[i] ;
if(used[prime[i]]) ans -- ;
else ans ++ ;
}
}
if(x > 1)
{
if(used[x]) ans -- ;
else ans ++ ;
}
return ans ;
}
void solve(int now)
{
int d = 0 ;
for(int i = 1 ; i <= now ; i ++) d = __gcd(d , a[i]) ;
if(cal(d) < 0)
for(int i = 1 ; i <= now ; i ++) a[i] /= d ;
}
int main()
{
int ans = 0 ;
scanf("%d%d" , &n , &m) ;
for(int i = 1 ; i <= n ; i ++) scanf("%d" , &a[i]) ;
for(int i = 1 ; i <= m ; i ++)
{
int x ;
scanf("%d" , &x) ;
used[x] = 1 ;
}
get_prime() ;
for(int i = n ; i >= 1 ; i --) solve(i) ;
for(int i = 1 ; i <= n ; i ++) ans += cal(a[i]) ;
printf("%d\n" , ans) ;
return 0 ;
}