题目链接:http://codeforces.com/problemset/problem/402/D
题意:
给你一个长度为n的数列a[i],又给出了m个“坏质数”b[i]。
定义函数f(s),其中p是s的最小质因子:
f(1) = 0
f(s) = f(s/p) + 1 (p不是坏质数)
f(s) = f(s/p) - 1 (p是坏质数)
你可以任意次数地进行操作:给a[1 to i]的每个数都除以gcd(a[1 to i])。
问你 ∑ f(a[i)最大为多少。
题解:
函数f(s)的实际意义是:
s的质因子中,好质数的个数 - 坏质数的个数。
每一次操作的实际意义是:
对于所选前缀中的每个数,从它的质因子中丢掉若干个好质数和坏质数。
显然,要想使 ∑ f(a[i)更大,则:
对于每次操作,当且仅当丢掉的坏质数的个数大于丢掉好质数的个数时,才会执行操作。
然后考虑操作顺序所带来的影响:
假设有i < j。
如果先执行操作opt(1 to i),再执行操作opt(1 to j):
由于第一次操作已经使得gcd(a[1 to i])变为1,并且区间[1,j]包含着[1,i]。
所以此时gcd(a[1 to j]) = 1,即第二次操作是无效的。
也就是说,a[i+1 to j]中的一些数,本身可以对答案做出贡献,却因为第一次操作而失效。
如果先执行操作opt(1 to j),再执行操作opt(1 to i):
因为第一次操作已经将a[i+1 to j]中可以除掉的坏质数全部除掉,并将a[1 to i]中的一部分坏质数除掉。
所以在第二次操作时,只要将a[1 to i]中剩下的坏质数除掉即可,不会有任何浪费。
所以从后往前贪心地操作就好啦。
复杂度分析:
(1)预处理出前缀gcd[i]:
O(n*logn)
(2)处理出所有操作完成后的数列a[i](要分解质因数):
O(n*sqrt(n))
(3)算出所有的f(a[i])(还要分解质因数):
O(n*sqrt(n))
AC Code:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <math.h> 5 #include <set> 6 #define MAX_N 5005 7 8 using namespace std; 9 10 int n,m; 11 int a[MAX_N]; 12 int g[MAX_N]; 13 set<int> st; 14 15 void read() 16 { 17 cin>>n>>m; 18 for(int i=1;i<=n;i++) 19 { 20 cin>>a[i]; 21 } 22 int x; 23 for(int i=1;i<=m;i++) 24 { 25 cin>>x; 26 st.insert(x); 27 } 28 } 29 30 int gcd(int a,int b) 31 { 32 return b==0 ? a : gcd(b,a%b); 33 } 34 35 void cal_g() 36 { 37 g[1]=a[1]; 38 for(int i=2;i<=n;i++) 39 { 40 g[i]=gcd(g[i-1],a[i]); 41 } 42 } 43 44 pair<int,int> cal_p(int x) 45 { 46 int good=0; 47 int bad=0; 48 for(int i=2,t=sqrt(x);i<=t;i++) 49 { 50 int cnt=0; 51 while(x%i==0) 52 { 53 x/=i; 54 cnt++; 55 } 56 if(cnt) 57 { 58 if(st.count(i)>0) bad+=cnt; 59 else good+=cnt; 60 } 61 } 62 if(x!=1) 63 { 64 if(st.count(x)>0) bad++; 65 else good++; 66 } 67 return pair<int,int>(good,bad); 68 } 69 70 bool check(int x) 71 { 72 pair<int,int> p=cal_p(x); 73 return p.first<p.second; 74 } 75 76 void cal_a() 77 { 78 int d=1; 79 for(int i=n;i>=1;i--) 80 { 81 if(check(g[i]/d)) d=g[i]; 82 a[i]/=d; 83 } 84 } 85 86 int cal_f() 87 { 88 int ans=0; 89 for(int i=1;i<=n;i++) 90 { 91 pair<int,int> p=cal_p(a[i]); 92 ans+=p.first-p.second; 93 } 94 return ans; 95 } 96 97 void work() 98 { 99 cal_g(); 100 cal_a(); 101 cout<<cal_f()<<endl; 102 } 103 104 int main() 105 { 106 read(); 107 work(); 108 }