码迷,mamicode.com
首页 > 其他好文 > 详细

20171206校内训练

时间:2017-12-07 22:37:23      阅读:302      评论:0      收藏:0      [点我收藏+]

标签:删除   dea   space   memset   总数   第一个   long   play   closed   

技术分享图片

技术分享图片

我们可以发现,所有数要么被删,要么+1(-1,0)是某个质数的倍数。

由于整段序列不能全部被删,所以第一个数或者最后一个数一定会被保留。

这里有6种情况c1-1,c1,c1+1,cn-1,cn,cn+1。

考虑对于每一种情况分别处理。设这个数为x

那么其中的每个数都必须被删或者是x的质因数的倍数。

枚举每个x的质因数(记为y)

于是我们可以dp

dp[i][0/1/2]表示前i个还没开始删除/正在删除/删完了的最小花费。

dp[i][0/1/2]==INF表示无法把前i个数做为y的倍数。显然dp[i][1]和dp[i][2]不可能==INF

dp[i][0]=B(x==c1-1,c1+1,cn-1,cn+1恐怖操作)或dp[i][0]=0(x==c1,cn不进行恐怖操作)

dp[i][0]=dp[i-1][0](c[i]%y==0,当前第i个数不用任何花费)dp[i][0]=dp[i-1][0]+b(c[i]±1%y==0,当前第i个数需要进行恐怖操作)

dp[i][1]=min(dp[i-1][0],dp[i-1][1])(当前第i个数可以从还没开始删除的第i-1个数或者正在删除的第i-1个数删除第i个数而来)

dp[i][2]显然和dp[i][0]是一样的,把dp[i-1][0]换成min(dp[i-1][1],dp[i-1][2])(第i-1个数可以正在删除或者本来就结束了)

把所有有关c1的处理好后翻转序列继续处理即可。

技术分享图片
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
long long dp[1001000][3];int c[1010000];
int prime[1000000],tot=0;bool isprime[1000000];
int q[1000000],top=0;
int n;long long A,B;
long long INF=2893606913523066920ll;
long long ans=2893606913523066920ll;
void Prime()
{
    for(int i=2;i<=100000;i++)
    {
        if(!isprime[i])prime[++tot]=i;
        for(int j=1;j<=tot&&prime[j]*i<=100000;j++)
        {
            isprime[prime[j]*i]=1;if(i%prime[j]==0)break;
        }
    }
}
void fj(int x)
{
    for(int i=1;prime[i]*prime[i]<=x;i++)
    {
        if(x%prime[i]==0)q[++top]=prime[i];
        while(x%prime[i]==0)x/=prime[i];
    }
    if(x!=1)q[++top]=x;
} 
void Solve(int x,int ok)
{
    top=0;fj(x);
    for(int i=1;i<=top;i++)
    {
        memset(dp,40,sizeof(dp));
        dp[1][0]=ok;
        for(int j=2;j<=n;j++)
        {
            if(c[j]%q[i]==0)dp[j][0]=dp[j-1][0];
            else if(dp[j-1][0]!=INF&&((c[j]+1)%q[i]==0||(c[j]-1)%q[i]==0))dp[j][0]=dp[j-1][0]+B;
            dp[j][1]=min(dp[j-1][1],dp[j-1][0])+A;
            if(c[j]%q[i]==0)dp[j][2]=min(dp[j-1][2],dp[j-1][1]);
            else if((c[j]+1)%q[i]==0||(c[j]-1)%q[i]==0)dp[j][2]=min(dp[j-1][2],dp[j-1][1])+B;
        }
        ans=min(ans,min(dp[n][0],min(dp[n][1],dp[n][2])));
    }
}
int main()
{
//    freopen("seq.in","r",stdin);freopen("seq.out","w",stdout);
    Prime();
    scanf("%d%lld%lld",&n,&A,&B);
    for(int i=1;i<=n;i++)scanf("%d",&c[i]);
    Solve(c[1]-1,B);Solve(c[1],0);Solve(c[1]+1,B);
    for(int i=1;i<=n/2;i++)swap(c[i],c[n-i+1]);
    Solve(c[1]-1,B);Solve(c[1],0);Solve(c[1]+1,B);
    printf("%lld",ans);
    return 0;
} 
View Code

技术分享图片

显然需要排序。

考虑枚举有多少个高度最大的墙(显然是把高的墙垫高),剩下的墙的最小高度的最大值可以二分求出。

如何二分?

找到一块墙(记为x),当前剩余砖块能把x以前的墙全部叠到墙x的高度以上而又叠不到墙x+1的高度。

技术分享图片

然后我们就能求出高度达到墙x的高度所需的砖块(总数-前缀和),然后能计算出可以达到的最小高度(最后剩下的砖块/x)

细节注意一下:二分右端点不能超过你枚举的多少个高度最大的墙。要保证你的砖块能够把你枚举的墙给搭到H。可以不把任何一块墙给搭到H。

打完后突然发现不用二分,根据枚举的高度最大的墙越多,剩下的墙的最小高度越小的单调性来解题即可(虽然排序还是O(nlogn))

技术分享图片
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
long long h[100100],s[100100];
int n;long long H;int a,b;long long m;
long long ans=-1;
int main()
{
//  freopen("wall.in","r",stdin);freopen("wall.out","w",stdout);
    scanf("%d%lld%d%d%lld",&n,&H,&a,&b,&m);
    for(int i=1;i<=n;i++)scanf("%lld",&h[i]);
    sort(h+1,h+n+1);
    for(int i=1;i<=n;i++)s[i]=s[i-1]+h[i];
    if(H*n-s[n]<=m){printf("%d",n*(a+b));return 0;}
    for(int i=2;i<=n+1;i++)
    {
        if((n-i+1)*H-(s[n]-s[i-1])>m)continue;
        long long left=m-((n-i+1)*H-(s[n]-s[i-1]));
        int l=1,r=i;
        while(l<r)
        {
            int mid=(l+r)>>1;
            if(h[mid]*mid-s[mid]<=left)l=mid+1;
            else r=mid;
        }
        long long Min=(left-(h[l-1]*(l-1)-s[l-1]))/(l-1)+h[l-1];
        ans=max(ans,Min*b+(n-i+1)*a);
    }
    printf("%lld",ans);
    return 0;
}
View Code

 

20171206校内训练

标签:删除   dea   space   memset   总数   第一个   long   play   closed   

原文地址:http://www.cnblogs.com/lher/p/8001267.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!