Codeforces 402D Upgrading Array:貪心 + 數學

Leohh發表於2018-01-06

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

 

相關文章