• 洛谷P1912 [NOI2009]诗人小G


    题目链接:P1912 [NOI2009]诗人小G
    题目大意:

    给定 (N) 句诗和行标准长度 (L) ,定义一行长度为 (S) 的诗(包含空格等符号的总个数)的不协调度为 (|L-S|^P) ,要求输出一种排版方案,使得每句诗都完整地在一行上,句与句之间以空格分隔,并且所有行的不协调度的和最小。
    (Nleq 10^5) , (Lleq3*10^6) , (Pleq10) ,每句诗的字符数 (leq30)

    思路:
    (F[i]) 为前 (i) 句诗的最小不协调度,(sum[i]) 为前 (i) 句诗的长度和,则有朴素 (O(N^2)) DP:

    [F[i]=min_{0leq j<i}{F[j]+|sum[i]-sum[j]+i-j-1-L|^P} ]

    (i-j-1) 为空格个数),考虑对DP进行优化,记 (val(j,i)=|sum[i]-sum[j]+i-j-1-L|^P) ,观察这个式子有没有特殊性质,将 (val(j,i)) 写成 (|(sum[i]+i)-(sum[j]+j)-1-L|)(sum[i]+i) 是一个关于 (i) 单调递增的式子,由于 (val(j,i)) 有不定的高次指数 (P) ,不适合用单调队列或斜率优化,考虑 (val(i,j)) 是否满足四边形不等式:

    [val(j,i+1)+val(j+1,i)geq val(j,i)+val(j+1,i+1) ]

    (u=val(j,i),v=val(j+1,i)) ,由于前缀和的递推关系,记 (a[i]) 为第 (i) 句诗的长度,不等式可转化为

    [|v|^P-|v+(a[i+1]+1)|^Pgeq |u|^P-|u+(a[i+1]+1)|^P ]

    由于 (u>v),该式等价于证对于任意常数 (c) ,函数 (f(x)=|x|^P-|x-c|^P) 单调递减,这个式子可以通过导数很容易地证明(其实是我不想写了),放到Geogebra上可以感性地理解一下其正确性

    知道 (val(j,i)) 满足四边形不等式后,我们就可以得到 (F[x]) 具有决策单调性,用队列维护决策三元组,能够 (O(NlogN)) 解决本题。

    实现细节:

    • 题目要求在答案超过 (10^{18}) 的时候输出"Too hard to arrange",由于这个值long long也可能存不下,(F) 数组的类型要开成long double。
    • 实现决策单调性DP的时候由于后面要统计方案,队头决策的左端点在被修改后要及时还原,同时插入决策前先判断其左右区间是否合法。
    • 这道题我被卡常了,注意输出排版的时候尽量不要用递归,将决策队列处理成分段方式存在数组里后循环输出,另外请手写快速幂,cmath的pow太慢了容易TLE。
    • 每一行诗的结尾不要留空格

    Code:

    #pragma optimize(O3)
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<fstream>
    #include<cstring>
    #define ll long long
    #define ld long double
    #define N 100100
    #define RG register
    using namespace std;
    struct choise{
        int i,l,r;
    }q[N];
    ld dp[N],sum[N],L;
    int T,n,p,l,r,out[N];
    char s[N][35];
    void _printf(string s){
        int k=s.size();
        for(register int i=0;i<k;i++)putchar(s[i]);
    }
    inline ld qpow(RG ld a,RG int b){
        RG ld ret=1;
        for(;b;b>>=1){
            if(b&1)ret*=a;a*=a;
        }
        return ret;
    }
    inline ld val(RG int j,RG int i){
        return qpow(abs(sum[i]-sum[j]+i-j-1-L),p);
    }
    inline bool better(int a,int b,int i){
        return dp[a]+val(a,i)<=dp[b]+val(b,i);
    }
    inline int binary_search(int l,int r,int a,int b){
        RG int L=l,R=r;
        while(L<R){
            int mid=(L+R)>>1;
            if(better(a,b,mid))R=mid;
            else L=mid+1;
        }
        return L;
    }
    int main(){
        int pos,temp;
        cin>>T;
        while(T--){
            cin>>n>>L>>p;
            for(int i=0;i<n;i++){
                scanf("%s",s[i]);
                sum[i+1]=sum[i]+strlen(s[i]);
            }
            l=r=0,q[0]={0,1,n};
            for(int i=1;i<=n;i++){
                while(l<=r&&q[l].r<i)l++;
                temp=q[l].l,q[l].l=i;
                dp[i]=dp[q[l].i]+val(q[l].i,i);
                pos=n+1;
                while(l<=r&&better(i,q[r].i,q[r].l))pos=q[r--].l;
                if(l<=r&&better(i,q[r].i,q[r].r))
                    pos=binary_search(q[r].l,q[r].r,i,q[r].i);
                if(l<=r)q[r].r=pos-1,q[l].l=temp;
                if(pos<=n)q[++r]={i,pos,n};
            }
            if(dp[n]>1e18)cout<<"Too hard to arrange
    ";
            else{
                cout<<(ll)dp[n]<<endl;
                int up=r;
                temp=0,out[0]=n;
                while(out[temp]){
                    while(up>=0&&q[up].l>out[temp])up--;
                    out[++temp]=q[up].i;
                }
                for(int i=temp;i>=1;i--){ 
                    for(int j=out[i];j<out[i-1];j++){
                        _printf(s[j]);
                        if(j!=out[i-1]-1)putchar(' ');
                    }
                    putchar('
    ');
                }
            }
            puts("--------------------");
        }
        return 0;
    }
    
  • 相关阅读:
    dart中Map类型详解
    洛谷P1582 倒水(二进制)
    maven依赖 临时转阿里云镜像
    java jdbc 连接数据库
    java 常用类
    java 异常处理
    java 对象 this static 封装
    java 面对对象 内存分析
    团队开发 git
    java 代码块
  • 原文地址:https://www.cnblogs.com/Neal-lee/p/14048094.html
Copyright © 2020-2023  润新知