首页 > 其他分享 >P9523

P9523

时间:2023-12-23 21:44:29浏览次数:16  
标签:pre lcp min int len P9523 dp

先 orz oyds。但是为什么没有 oyds 的简单预处理做法啊。

区间 dp。\(dp_{i,j}\) 表示凑出区间 \([i,j]\) 的最小代价。考虑枚举当前区间 \([i,j]\) 与 \(k\),表示 \([i,j]\) 在区间 \([p,j]\) 中出现了 \(k\) 次,且 \(p\) 取得最大值。则有转移:

\[\min(dp_{p,j},dp_{i,j}+B+k\times C+(j-p+1-k\times len)\times A)\to dp_{p,j} \]

以及最基本的:

\[\min(dp_{i+1,j},dp_{i,j-1},dp_{i,j})\to dp_{i,j} \]

这部分复杂度为 \(O(n\sum\frac{n}{i})=O(n^2\ln n)\)。

现在就要考虑怎么找这个 \(k\) 对应的 \(p\)。考虑维护 \(S\) 每两个后缀的 \(\operatorname{lcp}\)。简单 dp 即可。然后维护对于一对 \((i,k)\) 的 \(\min j\) 使得 \(\operatorname{lcp}(S[i:n],S[j:n])\ge k\)。这个东西可以先记录 \(=k\) 的情况,然后求后缀最大值。

这部分 \(O(n^2)\)。

简单好写。

code:

点击查看代码
int n,m,lcp[N][N],pre[N][N];
ll A,B,C,dp[N][N];
char s[N];
void Yorushika(){
	scanf("%d%s%lld%lld%lld",&n,s+1,&A,&B,&C);
	drep(i,n,1){
		drep(j,i-1,1){
			lcp[i][j]=s[i]==s[j]?lcp[i+1][j+1]+1:0;
			lcp[i][j]=min(lcp[i][j],i-j);
			if(!pre[i][lcp[i][j]])
				pre[i][lcp[i][j]]=j;
		}
		drep(j,n,1){
			pre[i][j]=max(pre[i][j],pre[i][j+1]);
		}
	}
	mems(dp,0x3f);
	rep(i,1,n){dp[i][i]=A;}
	rep(len,1,n){
		rep(i,1,n-len+1){
			int j=i+len-1;
			dp[i][j]=min({dp[i][j],dp[i+1][j]+A,dp[i][j-1]+A});
			int p=i;
			rep(k,1,j/len){
				p=pre[p][len];
				if(!p)break;
				dp[p][j]=min(dp[p][j],dp[i][j]+B+(k+1)*C+(j-p+1-(k+1)*len)*A);
			}
		}
	}
	printf("%lld\n",dp[1][n]);
}
signed main(){
	int t=1;
	//	scanf("%d",&t);
	while(t--)
		Yorushika();
}

标签:pre,lcp,min,int,len,P9523,dp
From: https://www.cnblogs.com/yinhee/p/P9523.html

相关文章