zoukankan      html  css  js  c++  java
  • [APIO2014] 序列分割

    Description

    给定一个长度为 (n) 的序列,需要将其划分成 (k+1) 份,每次划分时会将一个段划分成两个段,本次操作的得分为划分成的两个段的和的乘积。求最大得分和为多少。

    Solution

    容易证明操作顺序对答案没有影响,具体可以证明个结合律,代数系统里面有过类似的东西。

    (f(i,j)) 表示前 (j) 个数分了 (i) 次的最大得分,则显然有转移

    [f(i,j) leftarrow f(i-1,k) + s_k (s_j-s_k) ]

    转化一下,并记 (f_j = f(i,j), g_j = f(i-1,j)),则有

    [s_k^2 - g_k = s_j s_k - f_j ]

    于是,考虑设 (Y(k) = s_k^2 - g_k , K(j) = s_j, X(k) = s_k, B(j) = -f_j),斜率优化即可,维护一个斜率单调递增的单调队列。

    自闭原因:数组大小写反,卡精度

    #include <bits/stdc++.h>
    using namespace std;
    
    #define int long long 
    #define double long double
    const int N = 100005;
    const int M = 205;
    
    int n,k,a[N],s[N],q[N],f[N],g[N],head=0,tail=0;
    int from[M][N];
    
    double Y(int k)
    {
        return s[k]*s[k]-g[k];
    }
    double X(int k)
    {
        return s[k];
    }
    double K(int j)
    {
        return s[j];
    }
    double B(double Y,double K,double X)
    {
        return Y-K*X;
    }
    
    
    
    double slope(int p,int q)
    {
        double dy=Y(p)-Y(q);
        double dx=X(p)-X(q);
        if(abs(dx)<1e-9)
        {
            return -1e32;
        }
        else 
        {
            return dy/dx;
        }
    }
    
    signed main()
    {
        // freopen("p3648.in","r",stdin);
    
        ios::sync_with_stdio(false);
    
        cin>>n>>k;
        for(int i=1;i<=n;i++) cin>>a[i];
    
        for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i];
    
        for(int i=1;i<=k;i++)
        {
            for(int j=1;j<=n;j++) g[j]=f[j];
            head=0;
            tail=-1;
            q[0]=0;
            for(int j=1;j<=n;j++)
            {
                while(head<tail && slope(q[head],q[head+1])<=K(j)) ++head;
                if(head<=tail)
                {
                    int k=q[head];
                    f[j]=g[k]+s[k]*(s[j]-s[k]);
                    from[i][j]=k;
                }
                else f[j]=0;
                while(head<tail && slope(q[tail-1],q[tail])>=slope(q[tail],j)) --tail;
                q[++tail]=j;
    
            }
        }
    
        cout<<f[n]<<endl;
    
        int i=k, j=from[k][n];
        while(i)
        {
            cout<<j<<" ";
            j=from[--i][j];
        }
    }
    
    
  • 相关阅读:
    SPOJ 694 (后缀数组) Distinct Substrings
    POJ 2774 (后缀数组 最长公共字串) Long Long Message
    POJ 3693 (后缀数组) Maximum repetition substring
    POJ 3261 (后缀数组 二分) Milk Patterns
    UVa 1149 (贪心) Bin Packing
    UVa 12206 (字符串哈希) Stammering Aliens
    UVa 11210 (DFS) Chinese Mahjong
    UVa (BFS) The Monocycle
    UVa 11624 (BFS) Fire!
    HDU 3032 (Nim博弈变形) Nim or not Nim?
  • 原文地址:https://www.cnblogs.com/mollnn/p/14040409.html
Copyright © 2011-2022 走看看