[NOI 2009]诗人小G

Description

题库链接

给出 \(n\) 个字符串 \(s_i\),让你把这 \(n\) 个字符串顺序不变地分成若干行。每行字符串与字符串之间用空格隔开。假设一行的长度为 \(x\),该行的不美观度为 \(|x-l|^p\),其中 \(l,p\) 给定。让你最小化不美观度和。多测。

\(1\leq n\leq 10^5, \max |s_i|\leq 30, l\leq 3\times 10^6, 2\leq p\leq 10\)

Solution

\(f_i\) 表示 \(1\sim i\) 的字符串分为若干行后最小的不美观度和。令 \(sum_i\) 表示前 \(i\) 个字符串并且每个字符串后都加一个空格长度的前缀和。

可以得到转移方程 \(f_i=\min\limits_{0\leq j<i}\left\{f_j+|sum_i-sum_j-1-l|^p\right\}\)

\(w(j,i)=|sum_i-sum_j-1-l|^p\)。我们先证明 \(w\) 满足四边形不等式,实际上我们只需要证明 \(\forall a<b<c<d\) 都有 \(|d-a-l|^p+|c-b-l|^p\geq |d-b-l|^p+|c-a-l|^p\)

证明的话提供一个思路,由于 \(y=x^p\) 下凸,并且不等号两边 \(d-a+c-b=d-b+c-a\),可以画出这四个区间,并且最长和最短的区间都在不等号左端。用 \(l\) 去截四个区间时恒有上式成立。

如果不想感性证明,把上式用函数单调性的思想同样能得出结论。

因此 DP 方程是满足决策单调性的,可以用二分+单调队列来求解。这方面不清楚的可以戳这篇博客学习。

Code

#include <bits/stdc++.h>
#define ld long double
using namespace std;
const int N = 100000+5;

int t, n, L, p, sum[N], pre[N], q[N], head, tail, l[N], r[N], lst[N];
char ch[N][31];
ld f[N];

ld w(int i, int j) {
    ld ans = 1, a = abs(sum[j]-sum[i]-1-L); int b = p;
    while (b) {
        if (b&1) ans = ans*a;
        b >>= 1, a = a*a;
    }
    return ans;
}
void cal() {
    long long ans = 0;
    for (int i = n; i; i = lst[i]) ans += 1ll*w(lst[i], i);
    printf("%lld\n", ans);
}
void print(int x) {
    if (!x) return;
    print(lst[x]);
    for (int i = lst[x]+1; i <= x; i++)
        printf("%s%c", ch[i], " \n"[i == x]);
}
int main() {
    scanf("%d", &t);
    while (t--) {
        scanf("%d%d%d", &n, &L, &p);
        for (int i = 1; i <= n; i++) {
            scanf("%s", ch[i]);
            sum[i] = strlen(ch[i])+1+sum[i-1];
        }
        l[head = tail = 0] = 1, r[0] = n;
        for (int i = 1; i <= n; i++) {
            if (r[q[head]] < i) ++head;
            f[i] = f[q[head]]+w(q[head], i);
            lst[i] = q[head];
            if (f[q[tail]]+w(q[tail], n) < f[i]+w(i, n)) continue;
            while (head < tail && f[q[tail]]+w(q[tail], l[q[tail]]) >= f[i]+w(i, l[q[tail]])) --tail;
            int ll = l[q[tail]], rr = n, ans, mid;
            while (ll <= rr) {
                int mid = (ll+rr)>>1;
                if (f[q[tail]]+w(q[tail], mid) >= f[i]+w(i, mid)) rr = mid-1, ans = mid;
                else ll = mid+1;
            }
            r[q[tail]] = ans-1;
            l[q[++tail] = i] = ans, r[q[tail]] = n;
        }
        if (f[n] > 1e18) puts("Too hard to arrange");
        else {
            cal();
            print(n);
        }
        puts("--------------------");
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/NaVi-Awson/p/12384595.html
今日推荐