P1912 [NOI2009] 诗人小G

题目描述

给定 \(n\) 个字符串,求分成任意段之后的最小价值。

思路

由于分成任意段,所以只需要一维DP即可实现,\(f_i\) 表示到第 \(i\) 个字符串为止分段后的最小价值,很容易得出形如 \(f_i=\min f_j+w(j+1,i)\) 的状态转移方程,其中价值求法可以使用前缀和+快速幂的方式以极快的速度求出,最终方程为 $$f_i=\min f_j+\left| sum_i-sum_j-L-1 \right|^p$$ 其中 \(sum_i\) 表示第 \(i\) 个字符串之前的所有字符串加空格,注意第一个字符串前无空格,所以要再进行减一操作。最终时间复杂度为 \(O(n^2)\) 考虑优化。

由于 \(p\) 次幂的存在,我们无法将费用转化为平方项加交叉项的形式,不能使用斜率优化实现,那么考虑决策单调性,我们只要证明了 \(w(j+1,i)=\left\| sum_i-sum_j-L-1 \right\|^p\) 满足四边形不等式即可证明其状态转移方程具有决策单调性,即证 \(w(j+1,i)+w(j,i+1) \ge w(j,i)+w(j+1,i+1)\)

如何证明,展开上式即为

\[\lvert sum_i-sum_{j+1}-L-1 \rvert ^p+ \lvert sum_{i+1}-sum_j-L-1 \rvert ^p \ge \lvert sum_i-sum_j-L-1 \rvert ^p+\lvert sum_{i+1}-sum_{j+1}-L-1 \rvert ^p \]

换元令 \(u=sum_i,v=sum_{i+1}=u+a_i,x=sum_j,y=sum_{j+1}=x+a_j\),上式即可变为

\[\lvert u-y-L-1 \rvert^p+\lvert v-x-L-1 \rvert^p \ge \lvert u-x-L-1 \rvert^p+\lvert v-y-L-1 \rvert^p \]

即证明

\[\lvert u-x-a_j-L-1 \rvert^p+ \lvert u+a_i-x-L-1 \rvert^p \ge \lvert u-x-L-1 \rvert^p+ \lvert u+a_i-x-a_j-L-1 \rvert^p \]

\(d=u-x-L-1\),上式即

\[\lvert d-a_j \rvert^p+ \lvert d+a_i \rvert^p \ge \lvert d \rvert^p+ \lvert d+a_i-a_j \rvert^p \]

移项得

\[\lvert d-a_j \rvert^p- \lvert d \rvert^p \ge \lvert d+a_i-a_j \rvert^p- \lvert d+a_i \rvert^p \]

等式两边均可表示为 \(f(x)=\lvert x-a_j \rvert^p- \lvert x \rvert^p\) 的函数形式,而该函数非严格单调递减,因为 \(a_i \ge 1\),所以 \(d \le d+a_i\),所以上述不等式成立,所以转移方程满足四边形不等式,具有决策单调性。

当然,本题四边形不等式证明较为复杂,可以考虑打表观察决策点从而得到决策点调性,更加快速但不能保证正确性。

知道这个性质之后,就可以套模板了,因为本题转移时与先前决策点相关且仅有一维转移,所以不便于使用分治实现,故使用单调队列来维护决策点,时间复杂度 \(O(n \log n)\),可以通过本题。

本题一个坑点,上限会卡到 \(1e18\),使用 \(long long\) 会超出范围,因为其对精度没有要求,需要使用 \(long double\) 来实现,输出的时候再转换为 \(long long\) 形式。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int M=500010;
long double dp[M];
ll l,p,n,ans[M],a[M],sum[M],q[M],h,t;
long double qpow(long double a,ll b)
{
	long double res=1;
	while(b)
	{
		if(b&1LL) res=res*a;
		b>>=1LL;
		a=a*a;
	}
	return res;
}
long double ct(int x,int y)
{
	return dp[x]+qpow(abs(sum[y]-sum[x]+y-x-1-l),p);
}
int solve(int x,int y)
{
	if(ct(x,n)<ct(y,n)) return n+1;
    int le=y,re=n,ans=-1;
    while(le<=re)
	{
        int mid=(le+re)>>1;
        if(ct(y,mid)<=ct(x,mid)) 
        {
			ans=mid;
			re=mid-1;
		}
        else 
		{
			le=mid+1;
		}
    }
    return ans;
}
int sta[M],m;
char s[M][32];
int main()
{
	scanf("%d",&m);
	while(m--)
	{
		scanf("%d%d%d",&n,&l,&p);
		for(int i=1;i<=n;i++)
		{
			scanf("%s",s[i]);
			a[i]=strlen(s[i]);
			sum[i]=sum[i-1]+a[i];
			ans[i]=0;
			dp[i]=1e19;
		}
		h=1,t=0;
		q[++t]=0;
		for(int i=1;i<=n;i++)
		{
			while(h<t&&solve(q[h],q[h+1])<=i) h++;
			ans[i]=q[h];
			dp[i]=ct(q[h],i);
			while(h<t&&solve(q[t-1],q[t])>=solve(q[t],i)) t--;
			q[++t]=i;
		}
		if(dp[n]>1e18)
		{
			printf("Too hard to arrange\n");
		}
		else
		{
			printf("%lld\n",(ll)dp[n]);
			int top=0;
			for(int i=n;i;i=ans[i])
			{
				sta[++top]=i;
			}
			sta[++top]=0;
			for(int i=top;i>1;i--)
			{
				for (int j=sta[i]+1;j<sta[i-1];j++)
				{
					printf("%s ",s[j]);
				}
				printf("%s",s[sta[i-1]]); 
				printf("\n");
			}
		}
		printf("--------------------\n");
	}
    return 0;
}
posted @ 2025-08-21 13:04  Naoxiaoyu  阅读(19)  评论(0)    收藏  举报