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 }

原文地址:https://www.cnblogs.com/Leohh/p/8215403.html

时间: 2024-10-12 00:32:19

Codeforces 402D Upgrading Array:贪心 + 数学的相关文章

CodeForces 402D Upgrading Array

http://codeforces.com/problemset/problem/402/D 题意: 给了一串序列,计算出这个序列最小的∑f(a[i]).定义了f(s),给了个公式.可以进行一系列的操作,这个操作为从1~r 这些数都除以他们的gcd.让你最后算出最后最小∑f(a[i])是多少.当然如果不需要进行操作就可以达到最小,也是可以的. 思路: 首先要看出所定义的f(s)的那个公式其实就是代表的是将s分解为质因数的乘积之后,好的素数有几个,坏的素数为几个,好的+1,坏的-1. 那么如果要使

Codeforces 494B Upgrading Array

http://codeforces.com/problemset/status 题意:给一个数组,和一个坏质数集合,可以无数次地让1到i这些所有数字除以他们的gcd,然后要求Σf(a[i])的最大值,其中 f(x)=f(x/p)+1,p为x的最小质数,且p不为坏质数 f(x/p)-1 ,p为x的最小质数,且p为坏质数 思路:我们考虑,如果f[j]取了1到j的gcd,那么对后面的决策并没有任何影响,因为我们统计的贡献,只统计1到i这个部分,不统计i以后的部分. 略坑,不知道第一次被什么卡了超时..

codeforces 402D D. Upgrading Array(dp+数论)

题目链接: codeforces 402D 题目大意: 给出一个数列,可以进行一种操作将某一个前缀除去他们的gcd,有一个函数f(x),f(1) = 0 , f(x) = f(x/p)+1,f(x) = f(x/p)-1(p是坏素数)问 ∑i≤nf(a[i]) 的最大值 题目分析: 首先根据那个递推式我们知道结果就是每个数的f(x)就是它的质因数当中的好素数的个数减去坏素数的个数. 然后我们知道如果某个gcd中好素数的个数小于坏素数的个数,那么我们把它除掉一定可以得到更好的结果,那么如果从最后一

贪心/数学 Codeforces Round #212 (Div. 2) A. Two Semiknights Meet

题目传送门 1 /* 2 贪心/数学:还以为是BFS,其实x1 + 4 * k = x2, y1 + 4 * l = y2 3 */ 4 #include <cstdio> 5 #include <algorithm> 6 #include <cstring> 7 using namespace std; 8 9 const int MAXN = 11; 10 const int INF = 0x3f3f3f3f; 11 char s[MAXN][MAXN]; 12 1

codeforces 482B. Interesting Array【线段树区间更新】

题目:codeforces 482B. Interesting Array 题意:给你一个值n和m中操作,每种操作就是三个数 l ,r,val.就是区间l---r上的与的值为val,最后问你原来的数组是多少?如果不存在输出no 分析:分析发现要满足所有的区间,而一个点上假如有多个区间的话,这个点的值就是所有区间或的值,因为只有这样才能满足所有区间的,把所有位上的1都保存下来了,那么可以发现用线段树来维护,但是那么怎么判断满不满足条件呢?可以也用线段树,更新了之后在整个维护一遍看看满不满足题意,如

CodeForces 449C Jzzhu and Apples 数学+素数

这道题目晚上本来就花了很多把都××了,着实觉得自己思路没错啊,回顾一下思路,给你n个数,分成两两组合一对,分成最多组如何分,但是组合的两个数 不能互素,所以呢 偶数肯定是好的了,所以先放着,先把素数给搞定,10^5所以枚举所有包含该素数因子的数,如果刚好分组则最好,不然的话其中有偶数的踢掉一个给下面的偶数处理部分,最后再处理偶数的部分,这样肯定满足组数最多,完全没有问题,后来方法确实是没问题啊,只是代码有问题,我靠!真是脑残!,今天看到一位大牛的想法,我跟他是一样的,只是代码写搓了,后来改了又改

hdu 4803 Poor Warehouse Keeper(贪心+数学)

题目链接:hdu 4803 Poor Warehouse Keeper 题目大意:有以个屏幕可以显示两个值,一个是数量x,一个是总价y.有两种操作,一种是加一次总价,变成x,x+y:一种是加一个数量,这要的话总价也会相应加上一个的价钱,变成x+1,y+y/x.总价显示的为取整后的整数,小数部分忽略.给定一个目标x,y,初始状态为1,1,求最少需要多少次可以目标状态,不可以达到的话输出-1. 解题思路:如果是加一次总价的话,单价就在变大:如果是加一次数量的话,单价是不变的.总而言之,单价是只会往上

Codeforces Round #300——C贪心——Tourist&#39;s Notes

Description A tourist hiked along the mountain range. The hike lasted for n days, during each day the tourist noted height above the sea level. On the i-th day height was equal to some integer hi. The tourist pick smooth enough route for his hike, me

Codeforces 482B Interesting Array(线段树)

题目链接:Codeforces 482B Interesting Array 题目大意:给定一个长度为N的数组,现在有M个限制,每个限制有l,r,q,表示从a[l]~a[r]取且后的数一定为q,问是 否有满足的数列. 解题思路:线段树维护,每条限制等于是对l~r之间的数或上q(取且的性质,相应二进制位一定为1),那么处理完所有的 限制,在进行查询,查询对应每个l~r之间的数取且是否还等于q.所以用线段树维护取且和,修改为或操作. #include <cstdio> #include <c