題目連結: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 }