zoukankan      html  css  js  c++  java
  • 【洛谷P1912】诗人小G

    题目

    题目链接:https://www.luogu.com.cn/problem/P1912
    小 G 是一个出色的诗人,经常作诗自娱自乐。但是,他一直被一件事情所困扰,那就是诗的排版问题。
    一首诗包含了若干个句子,对于一些连续的短句,可以将它们用空格隔开并放在一行中,注意一行中可以放的句子数目是没有限制的。小 G 给每首诗定义了一个行标准长度(行的长度为一行中符号的总个数),他希望排版后每行的长度都和行标准长度相差不远。显然排版时,不应改变原有的句子顺序,并且小 G 不允许把一个句子分在两行或者更多的行内。在满足上面两个条件的情况下,小 G 对于排版中的每行定义了一个不协调度, 为这行的实际长度与行标准长度差值绝对值的 (P) 次方,而一个排版的不协调度为所有行不协调度的总和。
    小 G 最近又作了几首诗,现在请你对这首诗进行排版,使得排版后的诗尽量协调(即不协调度尽量小),并把排版的结果告诉他。
    (Tleq 10,nleq 10^5,Lleq 3 imes 10^6,Pleq 10,) 字符串长度 (leq 30)

    思路

    ( ext{len}_i) 表示字符串的长度前缀和。可以直接把后面的空格长度算进去。
    (f_i) 表示前 (i) 句诗的最小不协调度,显然有

    [f_i=min_{j<i}(f_j+| ext{len}_i- ext{len}_j-1-m|^P) ]

    手画分类讨论一下不难发现,对于 (j<i)(j) 的决策点一定小于等于 (i) 的决策点。也就是满足四边形不等式。
    那么直接用单调队列维护一下就好了。要转移到 (i) 的时候,就不断检查队首,如果队首前两位的函数交点在 (i) 之前就弹出。
    插入 (i) 的时候不断检查队尾,如果队尾的两个元素的交点在队尾和 (i) 的交点后就弹出。
    判断答案超过 (10^{18}) 可以直接用 long double,这样如果超过了 (10^{18}) 虽然会损失精度导致结果错误,但是正确结果一定是超过 (10^{18})。直接判断无解即可。
    时间复杂度 (O(Tnlog n))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef long double ld;
    
    const int N=100010;
    const ld Inf=1e18;
    int Q,n,m,p,hd,tl,g[N],q[N],len[N],pre[N];
    ld f[N];
    char s[N][32];
    
    ld fpow(ld x,ll k)
    {
    	ld ans=1;
    	for (;k;k>>=1,x=x*x)
    		if (k&1) ans=ans*x;
    	return ans;
    }
    
    ld calc(int i,int j)
    {
    	return f[j]+fpow(abs(len[i]-len[j]-1-m),p);
    }
    
    int binary(int i,int j)
    {
    	int l=j,r=n,mid;
    	while (l<=r)
    	{
    		mid=(l+r)>>1;
    		if (calc(mid,i)<calc(mid,j)) l=mid+1;
    			else r=mid-1;
    	}
    	return l-1;
    }
    
    void dfs(int n)
    {
    	if (!n) return;
    	dfs(pre[n]);
    	for (int i=pre[n]+1;i<n;i++)
    		printf("%s ",s[i]);
    	printf("%s
    ",s[n]);
    }
    
    int main()
    {
    	scanf("%d",&Q);
    	while (Q--)
    	{
    		memset(f,0,sizeof(f));
    		hd=tl=1; q[1]=0;
    		scanf("%d%d%d",&n,&m,&p);
    		for (int i=1;i<=n;i++)
    		{
    			scanf("%s",s[i]);
    			len[i]=len[i-1]+strlen(s[i])+1;
    		}
    		for (int i=1;i<=n;i++)
    		{
    			while (hd<tl && g[q[hd]]<i) hd++;
    			f[i]=calc(i,q[hd]); pre[i]=q[hd];
    			while (hd<tl && g[q[tl-1]]>=binary(q[tl],i)) tl--;
    			g[q[tl]]=binary(q[tl],i); q[++tl]=i;
    		}
    		if (f[n]>Inf) printf("Too hard to arrange
    ");
    			else printf("%lld
    ",(ll)f[n]),dfs(n);
    		printf("--------------------
    ");
    	}
    	return 0;
    }
    
  • 相关阅读:
    便携版Mysql安装
    Markdown 语法学习
    布局页中的特殊情况(比如说只有某页有的banner)
    Jsp Layout 布局页
    eclipse变量名自动补全
    java中的final关键字(2013-10-11-163 写的日志迁移
    java的重载(overload) (2013-10-11-163 写的日志迁移
    java 的多态(2013-10-11-163 写的日志迁移
    java中类与对象的概念(2013-05-04-bd 写的日志迁移
    java的一些相关介绍(2013-10-07-163 写的日志迁移
  • 原文地址:https://www.cnblogs.com/stoorz/p/14434944.html
Copyright © 2011-2022 走看看