Codeforces Round #236 (Div. 2)D题 Upgrading Array ;dp

dp

给出一个序列,a1~an;给m个坏素数。不在这m个坏素数之内的素数称为好素数。

对于序列中的每个数,对应一个函数值f(s)满足:如果s的最小质因子p是好素数,则,否则

另外,我们可以进行以下操作:选择一个r(1<=r<=n),计算a[1]到a[r]的最大公约数g = GCD(a[1], a[2], ..., a[r]),令...

我们可以无限次的进行以上操作,问,通过这些操作,我们能得到的   sum=最大值是多少。

我们首先预处理出前缀gcd,(我的代码里时dpg[]),通过题意,我们知道,前面数字进行的操作比后面的多或者一样。所以我们从后往前判断是否要进行操作。每次进行操作时,我们对这个gcd分解质因子,判断它们之中有多少好素数,多少个坏素数,如果这个操作有利于提高我们的sum,就进行此操作,否则就不进行。

同时记录到达当前位置时,已经除去的数,我用dive[]记录。

这样,我们统计到一个数时,如果之后一个位置的数进行过操作,则当前位置的数必然也进行过相同的操作,所以,我们可以用gcd[i]/dive[i+1]表示前i个数当前最大的gcd,进行上面同样的判断,并记录dive[i]。

代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#define INF 200000000
int dpg[5005],num[5005],dive[5005],prime[5005],n,m;
int gcd(int a,int b)
{
    if(a%b==0)
        return b;
    else
        return gcd(b,a%b);
}
int max(int a,int b)
{
    return a>b?a:b;
}
int min(int a,int b)
{
    return a>b?b:a;
}
int judge(int x)     //判断进行除x的操作,值的变化
{
    int i,j,sum;
    sum=0;
    for(i=1;i<=m;i++)
    {
        if(x%prime[i]==0)
        {
            while(x%prime[i]==0)     //这里是看了别人的代码,感觉这种处理好坏素数的方法更好
            {
                x/=prime[i];
                sum--;
            }
        }
    }
    for(i=2;i*i<=x;i++)
    {
        if(x%i==0)
        {
            while(x%i==0)
            {
                x/=i;
                sum++;
            }
        }
    }
    if(x>1)
    {
        sum++;
    }
    return sum;
}
int main()
{
    int i,j,k,l,add,re;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(i=1;i<=n;i++)
        {
            scanf("%d",&num[i]);
            if(i==1)
                dpg[i]=num[i];
            else
                dpg[i]=gcd(max(dpg[i-1],num[i]),min(dpg[i-1],num[i]));
        }
        for(i=1;i<=m;i++)
            scanf("%d",&prime[i]);
        dive[n+1]=1;
        add=0;
        for(i=1,re=0;i<=n;i++)
            re+=judge(num[i]);
        for(i=n;i>0;i--)
        {
            if(judge(dpg[i]/dive[i+1])<0)
            {
                dive[i]=dpg[i];
                add-=(judge(dpg[i]));
            }
            else
            {
                dive[i]=dive[i+1];
                add-=(judge(dive[i+1]));
            }
        }
        printf("%d\n",re+add);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值