• [NOI 2009]诗人小G


    Description

    题库链接

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

    (1leq nleq 10^5, max |s_i|leq 30, lleq 3 imes 10^6, 2leq pleq 10)

    Solution

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

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

    (w(j,i)=|sum_i-sum_j-1-l|^p)。我们先证明 (w) 满足四边形不等式,实际上我们只需要证明 (forall a<b<c<d) 都有 (|d-a-l|^p+|c-b-l|^pgeq |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
    ", 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], " 
    "[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;
    }
  • 相关阅读:
    移动端测试作业小集合 (6)
    移动端测试——手机常见操作的API (5)
    移动端测试——APP元素信息、事件操作、模拟手势API(4)
    移动端测试——APP元素定位操作 (3)
    移动端测试——App基础操作(2)
    移动端测试基础 (1)
    Python进阶-一切皆对象及type-object-class间的关系
    故障-解决pip安装mysqlclient、gevent报找不到cc或gcc错误问题
    Linux环境上部署Flask
    解决多版本共存时,python/pip等命令失效
  • 原文地址:https://www.cnblogs.com/NaVi-Awson/p/12384595.html
Copyright © 2020-2023  润新知