zoukankan      html  css  js  c++  java
  • Codeforces 402D Upgrading Array:贪心 + 数学

    题目链接: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 }
  • 相关阅读:
    CTSC2018滚粗记
    HNOI2018游记
    NOIWC 2018游记
    PKUWC2018滚粗记
    HNOI2017 游记
    NOIP2017题解
    [HNOI2017]抛硬币
    [HNOI2017]大佬
    NOIP难题汇总
    [NOI2013]树的计数
  • 原文地址:https://www.cnblogs.com/Leohh/p/8215403.html
Copyright © 2011-2022 走看看