多校一道KMP+DP的题

难啊,多校当时根本不会做
 

题目描述

White Cloud has a rectangle carpet of n*m. Grid (i,j) has a color colorA[i][j] and a cost costA[i][j].
White Rabbit will choose a subrectangle B of p*q from A and the color of each grid is colorB[0...p-1][0...q-1], the cost of B is the (maximum number in the corresponding subrectangle of costA*(p+1)*(q+1).
Then colorB is continuously translated and copied in an infinite times, that is, expand colorB into an infinite new matrix, colorC, which satisfies colorC[i][j]=colorB[i mod p][j mod q].
White Rabbit must ensure that colorA is a subrectangle of colorC.
You need to find the minimum cost way.
 

输入描述:

The first line of input contains two integers n,m(0<n*m <= 1000000)
For the next line of n lines, each line contains m lowercase English characters, denoting colorA.
For the next line of n lines, each line contains m integers in range [0,1000000000], denoting costA.

输出描述:

Print the minimum cost.

示例1

输入

2 5
acaca
acaca
3 9 2 8 7
4 5 7 3 1

输出

18

说明

choose subrectangle colorA[1...1][3...4]=ca, After copying unlimited copies

colorC=

cacacacaca ...

cacacacaca ...

cacacacaca ...

cacacacaca ...

cacacacaca ...

.........

colorA is a subrectangle of colorC

the cost is max(3,1)*(1+1)*(2+1).

 

题目大意:有一个n*m的矩阵A,每个位置有一个字符和一个权值,现在要找一个子矩阵,使得这个子矩阵是A的一个 循环节,并最小化子矩阵的权值最大值。

做法
首先要找到矩阵的一个最小的循环节,假设是p和q,那么最优解就一定是选一个p*q的子矩形。
如何求最小的循环节?行和列可以独立考虑。 求列的循环节时,我们可以对每一行做一次kmp,找到这些行的最大公共循环节。 求行的循环节时,我们可以对每一列做一次kmp,找到这些列的最大公共循环节。
接下来问题就变成了,求所有大小为p*q的子矩形的最大值的最小值。可以采用单调队列,求出 所有p*q的子矩形的最大值,然后找一个最小的即可。

 

标程:(肯定不是我写的哈哈)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<assert.h>
using namespace std;
int gi() {
	int w;char c;
	while (((c=getchar())<'0'||'9'<c)&&c!='-');
	w=c-'0';
	while ('0'<=(c=getchar())&&c<='9') w=(w<<3)+(w<<1)+c-'0';
	return w;
}
const int seed=131;
const int mod=1e9+7;
const int N=1e6+100;
int h[N],L[N],nxt[N];
inline int kmp(int *a,int n) {
	nxt[0]=0;
	int i,c,p;
	for (i=1;i<n&&a[i]==a[i+1];i++);i--;
	nxt[2]=i;c=2,p=2+nxt[2]-1;
	if (nxt[2]==n-1) return 1;
	for (i=3;i<=n;i++) {
		if (p<i||p-i+1<=nxt[i-c+1]) {
			nxt[i]=max(p-i+1,0);
			while (a[nxt[i]+i]==a[nxt[i]+1]) nxt[i]++;
			if (i+nxt[i]>n) return i-1;
			c=i,p=i+nxt[i]-1;
		}
		else nxt[i]=nxt[i-c+1];
	}
	return n;
}
int Val[N],Q[N];
#define val(x,y) Val[((x)-1)*m+(y)]
#define q(x,y) Q[((x)-1)*m+(y)]
int l[N],r[N],n,m;
int qq[N],w[N];
int key[26];
char s[N];
int main() {
	int i,j,a,b,ll,rr;char c;int ans=2147483647;
	n=gi(),m=gi();
	assert(0<n*m&&n*m<=1e6);
	for (i=0;i<26;i++) key[i]=rand();
	for (i=1;i<=n;i++) {
		scanf("%s",s+1);
		assert(strlen(s+1)==m);
		for (j=1;j<=m;j++) {
			c=s[j];assert('a'<=c&&c<='z');
			h[i]=(1LL*h[i]*seed+key[c-'a'])%mod;
			L[j]=(1LL*L[j]*seed+key[c-'a'])%mod;
		}
	}
	a=kmp(h,n);
	b=kmp(L,m);
	for (i=1;i<=n;i++) for (j=1;j<=m;j++) val(i,j)=gi(),assert(0<=val(i,j)&&val(i,j)<=1e9);
	for (i=1;i<=n;i++) l[i]=1,r[i]=0;
	for (j=1;j<b;j++)
		for (i=1;i<=n;i++) {
			while (l[i]<=r[i]&&val(i,j)>=val(i,q(i,r[i]))) r[i]--;
			q(i,++r[i])=j;
		}
	for (j=b;j<=m;j++) {
		ll=1,rr=0;
		for (i=1;i<a;i++) {
			if (l[i]<=r[i]&&q(i,l[i])<=j-b) l[i]++;
			while (l[i]<=r[i]&&val(i,j)>=val(i,q(i,r[i]))) r[i]--;
			q(i,++r[i])=j;
			while (ll<=rr&&val(i,q(i,l[i]))>=qq[rr]) rr--;
			qq[++rr]=val(i,q(i,l[i])),w[rr]=i;
		}
		for (i=a;i<=n;i++) {
			if (l[i]<=r[i]&&q(i,l[i])<=j-b) l[i]++;
			while (l[i]<=r[i]&&val(i,j)>=val(i,q(i,r[i]))) r[i]--;
			q(i,++r[i])=j;
			if (ll<=rr&&w[ll]<=i-a) ll++;
			while (ll<=rr&&val(i,q(i,l[i]))>=qq[rr]) rr--;
			qq[++rr]=val(i,q(i,l[i])),w[rr]=i;
			ans=min(ans,qq[ll]);
		}
	}
	cout<<1LL*(a+1)*(b+1)*ans<<endl;
	return 0;
}

 

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 27
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值