首页 > 代码库 > bzoj3675 [Apio2014]序列分割

bzoj3675 [Apio2014]序列分割

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3675

http://uoj.ac/problem/104

【题解】

当时想的时候猜了下从前往后分比较优。

后来证明了一下怎么分都一样。。可以把贡献式子拆开来分析。

这样分析完就可以得到贡献=Σ每两个块的和的积

那么我们整理可以得到贡献=Σ第i个块的和*(前面所有块的和)

那么有dp: f[i,j]表示到第i块,选择的数是a[1]...a[j]的贡献max。

那么直接dp有50分。

技术分享
# include <stdio.h>
# include <string.h>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 1e5 + 10, N = 210;
const int mod = 1e9+7;

# define RG register
# define ST static

int n, K, a[M];
ll s[M], f[N][M];
int pre[N][M];

inline void prt(int i, int j) {
    if(i == 1) return;
    prt(i-1, pre[i][j]);
    printf("%d ", pre[i][j]);
}

int main() {
    scanf("%d%d", &n, &K);
    for (int i=1; i<=n; ++i) scanf("%d", &a[i]), s[i] = s[i-1] + a[i];
    for (int i=2; i<=K+1; ++i) 
        for (int j=1; j<=n; ++j) {
            f[i][j] = -1e18;
            for (int k=1; k<j; ++k) {
                if(f[i][j] < f[i-1][k] + (s[j]-s[k])*s[k]) {
                    f[i][j] = f[i-1][k] + (s[j]-s[k])*s[k];
                    pre[i][j] = k;
                }
            }
        }
    printf("%lld\n", f[K+1][n]);
    prt(K+1, n);
    return 0;
}
View Code

肯定不能直接dp,需要优化

明显可斜率优化。

直接上即可。

uoj版本(输出方案,空间不限制)

技术分享
# include <stdio.h>
# include <string.h>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 1e5 + 10, N = 210;
const int mod = 1e9+7;

# define RG register
# define ST static

int n, K, a[M];
ll s[M], f[N][M];
int pre[N][M];
int q[M], head, tail;

inline void prt(int i, int j) {
    if(i == 1) return;
    prt(i-1, pre[i][j]);
    printf("%d ", pre[i][j]);
}

inline ll slop(int i, int k1, int k2) {
    return f[i-1][k1]-f[i-1][k2]+s[k2]*s[k2]-s[k1]*s[k1];
}

inline ll getdp(int i, int j, int x) {
    return f[i-1][x]+(s[j]-s[x])*s[x];
}

int main() {
    scanf("%d%d", &n, &K);
    for (int i=1; i<=n; ++i) scanf("%d", &a[i]), s[i] = s[i-1] + a[i];
    for (int i=2; i<=K+1; ++i) {
        head = 1, tail = 0;
        for (int j=1; j<=n; ++j) {
            while(head<tail && slop(i, q[head], q[head+1]) <= (s[q[head+1]] - s[q[head]]) * s[j]) ++head;
            f[i][j] = getdp(i, j, q[head]);
            pre[i][j] = q[head];
            while(head<tail && slop(i, q[tail-1], q[tail]) * (s[j] - s[q[tail]]) >= slop(i, q[tail], j) * (s[q[tail]] - s[q[tail-1]])) --tail;
            q[++tail] = j;
        }
    }
    printf("%lld\n", f[K+1][n]);
    prt(K+1, n);
    return 0;
}
View Code

bzoj版本(不输出方案,空间限制)

技术分享
# include <stdio.h>
# include <string.h>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 1e5 + 10, N = 210;
const int mod = 1e9+7;

# define RG register
# define ST static

int n, K, a[M];
ll s[M], f[2][M];
int q[M], head, tail;
int pre, cur;

inline ll slop(int k1, int k2) {
    return f[pre][k1]-f[pre][k2]+s[k2]*s[k2]-s[k1]*s[k1];
}

inline ll getdp(int j, int x) {
    return f[pre][x]+(s[j]-s[x])*s[x];
}

int main() {
    scanf("%d%d", &n, &K);
    for (int i=1; i<=n; ++i) scanf("%d", &a[i]), s[i] = s[i-1] + a[i];
    pre = 0, cur = 1;
    for (int i=2; i<=K+1; ++i) {
        head = 1, tail = 0;
        for (int j=1; j<=n; ++j) {
            while(head<tail && slop(q[head], q[head+1]) <= (s[q[head+1]] - s[q[head]]) * s[j]) ++head;
            f[cur][j] = getdp(j, q[head]);
            while(head<tail && slop(q[tail-1], q[tail]) * (s[j] - s[q[tail]]) >= slop(q[tail], j) * (s[q[tail]] - s[q[tail-1]])) --tail;
            q[++tail] = j;
        }
        swap(pre, cur);
    }
    printf("%lld\n", f[pre][n]);
    return 0;
}
View Code

 

bzoj3675 [Apio2014]序列分割