欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[BZOJ1563][NOI2009]诗人小G(DP+四边形不等式优化)

程序员文章站 2022-04-01 19:22:17
...

Address

https://www.lydsy.com/JudgeOnline/problem.php?id=1563

Solution

S 为句子长度的前缀和, f[i] 为前 i 个句子的最小不和谐度。

f[i]=minj=0i1{f[j]+|SiSj|P}

通过证 (gui) 明 (lv) ,我们得到:
|SiSj|P+|Si+1Sj+1|P|Si+1Sj|P+|SiSj+1|P

f 满足决策单调性
用一个栈记录决策表。
栈的每个元素是一个三元组 (l,r,x) 表示当前 f[l...r] 的最优决策为 x (三个元素都递增)。
一开始栈中有一个元素 (1,n,0)
考虑求出一个 f[i] 时如何用 f[i] 更新部分决策。
(1)对于栈顶 (l,r,x) ,如果 f[l]+|SlSi|P<f[l]+|SlSx|P ,那么 f[l] 取决策 ix 优,可退栈,然后继续执行(1),直到 li 或者 上式不满足为止。
(2)否则在 [max(l,i+1),r] 内找到一个点 k ,使得 f[k1] 取决策 xi 优, f[k] 取决策 ix 优。这时候将栈顶的 r 改成 k1 ,将三元组 (k,n) 入栈(当然,如果 k>n 就不要入栈)。可以用二分查找找到这样的 k
复杂度 O(Tnlogn)
注意 f 会超过 long long 范围,要使用 long double 。

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
using namespace std;
typedef long double ld;
const int N = 1e5 + 5, E = 33;
int n, l, p, top, sum[N];
char s[N][E];
ld f[N];
struct cyx {
    int l, r, x;
} stk[N];
ld w(int j, int i) {
    ld res = 1, a = abs(sum[i] - sum[j] + i - j - 1 - l);
    int b = p;
    while (b) {
        if (b & 1) res = res * a;
        a = a * a;
        b >>= 1;
    }
    return res;
}
void work() {
    int i, j, k, th = 1;
    scanf("%d%d%d", &n, &l, &p);
    For (i, 1, n) scanf("%s", s[i] + 1),
        sum[i] = sum[i - 1] + strlen(s[i] + 1);
    stk[top = 1] = (cyx) {1, n, 0};
    For (i, 1, n) {
        f[i] = f[stk[th].x] + w(stk[th].x, i);
        while (stk[top].l > i && f[i] + w(i, stk[top].l) <
            f[stk[top].x] + w(stk[top].x, stk[top].l)) top--;
        int L = max(stk[top].l, i + 1), R = stk[top].r;
        while (L <= R) {
            int mid = L + R >> 1;
            if (f[i] + w(i, mid) < f[stk[top].x]
                + w(stk[top].x, mid)) R = mid - 1;
            else L = mid + 1;
        }
        stk[top].r = L - 1;
        if (L <= n) stk[++top] = (cyx) {L, n, i};
        if (stk[th].r == i) th++;
    }
    if (f[n] > 1e18) puts("Too hard to arrange");
    else printf("%lld\n", (long long) (f[n] + 0.5));
    puts("--------------------");
}
int main() {
    int T; cin >> T;
    while (T--) work();
    return 0;
}