zoukankan      html  css  js  c++  java
  • 【斜率优化】bzoj3675-[Apio2014]序列分割&&Uoj104

    题目大意

    将一个长度为N的非负整数序列分割成k+l个非空的子序列,每次选择一位置分割后,将会得到一定的分数,这个分数为两个新序列中元素和的乘积。求最大的分数。
    [UOJ104]并输出任意一种方案

    思路

    显然,无论分割顺序如何,不会影响最后得到的结果。所以可以利用递推方程。(f[i][j])表示取前(i)个数,分割成(j)个序列能得到的最大分数。显然有:

    [f[i][k]=max(f[j][k-1]+sum[j]*(sum[i]-sum[k])) ]

    (Ans_{j_1}>Ans_{j_2})时,有:

    [f[j_1][k-1]+sum[j_1]*sum[i]-sum[j_1]^2 ]

    [gt ]

    [f[j_2][k-1]+sum[j_2]*sum[i]-sum[j_2]^2 ]

    (x[i]=f[i][k-1]-sum[i]^2,y[i]=sum[i])
    则有:

    [-sum[i]<frac{x[j_1]-x[j_2]}{y[j_1]-y[j_2]} ]

    注意点

    计算斜率的时候(x_1)可能等于(x_2),特判一下将斜率设为INF或-INF。不要忘记开long long。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long LL;
    const int MAXN=100100;
    const int MAXK=250;
    int n,k;
    LL sum[MAXN],x[2][MAXN],g[MAXN],y[MAXN],f[MAXN][2];
    int cur;
    
    void init()
    {
        scanf("%d%d",&n,&k);    
        for (int i=1;i<=n;i++) 
        {
            int tmp;
            scanf("%d",&tmp);
            y[i]=sum[i]=sum[i-1]+tmp;
            g[i]=-y[i];
        }
    }
    
    LL dp()
    {
        memset(f,0,sizeof(f));
        cur=0;
        for (int i=1;i<=n;i++) x[1-cur][i]=f[i][cur]-(sum[i]*sum[i]);
        for (int j=2;j<=k+1;j++)
        {
            cur=1-cur;
            int head=0,tail=1,que[MAXN];
            for (int i=j-1;i<=n;i++)//上一次至多分割为j-1部分,则至少从j-1开始 
            {
                while (head+1<tail && x[cur][que[head]]-x[cur][que[head+1]]<=g[i]*(y[que[head]]-y[que[head+1]])) head++;
                int best=que[head];
                f[i][cur]=f[best][1-cur]+sum[best]*(sum[i]-sum[best]);
                while (head+1<tail && (LL)(x[cur][que[tail-1]]-x[cur][i])*(y[que[tail-2]]-y[que[tail-1]])>=(LL)(x[cur][que[tail-2]]-x[cur][que[tail-1]])*(y[que[tail-1]]-y[i])) tail--;
                que[tail++]=i;
                x[1-cur][i]=f[i][cur]-(sum[i]*sum[i]);
            }
        }
        return (f[n][cur]);
    }
    
    void printans()
    {
        printf("%lld
    ",dp());
    }
    
    int main()
    {
        init();
        printans();
        return 0;   
    } 
    

    输出方案

    只需记录一下路径就好了。不过要注意,UOJ后面数据时间卡得非常可啪,所以我们不用斜率而是直接用乘法来计算,同时x数组y数组g数组也不要了直接套进去算,勉勉强强卡过了……[痛心疾首.jpg]
    顺带一提的是,这样的话BZOJ会T(咦?)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long LL;
    const int MAXN=100100;
    const int MAXK=250;
    int n,k;
    LL sum[MAXN],f[MAXN][2],fr[MAXN][MAXK];
    int cur;
    
    
    void init()
    {
        scanf("%d%d",&n,&k);    
        for (int i=1;i<=n;i++) 
        {
            int tmp;
            scanf("%d",&tmp);
            sum[i]=sum[i-1]+tmp;
        }
    }
    
    LL x(int m)
    {
    	return f[m][1-cur]-sum[m]*sum[m];
    }
    
    LL dp()
    {
        memset(f,0,sizeof(f));
        cur=0;
        for (int j=2;j<=k+1;j++)
        {
            cur=1-cur;
            int head=0,tail=1,que[MAXN];
            for (int i=j-1;i<=n;i++)//上一次至多分割为j-1部分,则至少从j-1开始 
            {
                while (head+1<tail && (f[que[head]][1-cur]-(LL)(sum[que[head]]*sum[que[head]])-f[que[head+1]][1-cur]+(LL)(sum[que[head+1]]*sum[que[head+1]])<=(LL)-sum[i]*(sum[que[head]]-sum[que[head+1]]))) head++;
                int best=que[head];
                f[i][cur]=f[best][1-cur]+sum[best]*(sum[i]-sum[best]);
                fr[i][j]=best;
                while (head+1<tail && (LL)(x(que[tail-1])-x(i))*(sum[que[tail-2]]-sum[que[tail-1]])>=(LL)(x(que[tail-2])-x(que[tail-1]))*(sum[que[tail-1]]-sum[i])) tail--;
                que[tail++]=i;
            }
        }
        return (f[n][cur]);
    }
    
    void printans()
    {
        printf("%lld
    ",dp());
        int ans[MAXK];
    	memset(ans,0,sizeof(ans));//不要忘记初始化★
        for (int i=k+1;i>=2;i--)
        {
        	ans[++ans[0]]=fr[n][i];
        	n=fr[n][i];
    	}
    	for (int i=ans[0];i>=1;i--) printf("%d ",ans[i]);
    }
    
    int main()
    {
        init();
        printans();
        return 0;   
    } 
    
  • 相关阅读:
    205. Isomorphic Strings
    8 旋转数组的最小数字
    303. Range Sum Query
    70. Climbing Stairs
    HDU 5971 Wrestling Match (二分图)
    URAL 2019 Pair: normal and paranormal (STL栈)
    URAL 2021 Scarily interesting! (贪心+题意)
    URAL 2018 The Debut Album (DP)
    HDU 5236 Article (概率DP+贪心)
    HDU 5241 Friends (大数)
  • 原文地址:https://www.cnblogs.com/iiyiyi/p/5728662.html
Copyright © 2011-2022 走看看