1. 程式人生 > >20171206校內訓練

20171206校內訓練

color -i sca turn 發現 span con esp 需要

技術分享圖片

技術分享圖片

我們可以發現,所有數要麽被刪,要麽+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校內訓練