Newcoder 140 K.carpet(hash+kmp+单调队列)

探讨了在给定矩阵A的基础上,寻找最优子矩阵B,使得B能生成由A无限复制得到的矩阵C,同时B的代价最小。通过求解最小循环节,使用KMP算法和优先队列优化计算过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

给出一个n×mn×m的矩阵AA,每个元素有权值,由A矩阵不断复制得到一个无限大矩阵CC,求A矩阵的一个p×qp×q子矩阵BB使得B矩阵也能生成CC矩阵且B的代价(BB中元素点权最大值(p+1)(q+1))最小

Input

第一行两个整数n,mn,m,之后输入一个只由小写字母组成的n×mn×m矩阵AA

(0nm106)

Output

输出BB的代价最小值

Sample Input

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

Sample Output

18

Solution

显然若B能生成CC矩阵,那么以B矩阵为循环节的任一AA矩阵的子矩阵均可以生成C,这些矩阵元素最大值和BB元素最大值相同且行列乘积大于B,显然代价比BB大,故要选择p,q最小的子矩阵BB,由于行列循环相互独立,故只需要求出行之间的最小循环节和列之间的最小循环节即为p,q的值,求行的循环节就把每行元素hashhash进而得到一个长度为nn的序列,用kmp即可得到这个序列的最小循环节,列的循环节同理

在求出p,qp,q的值之后,显然AA的任意一个p×q子矩阵均可生成CC,我们要找的是矩阵最大值最小的子矩阵,首先对每行用优先队列求出以(i,j)结尾的长度为qq的子段的ai,j最大值bi,jbi,j,再对每行用优先队列求出以(i,j)(i,j)结尾的长度为pp的子段的bi,j最大值,这个最大值即为以(i,j)(i,j)为右下角的p×qp×q子矩阵元素最大值,以此更新答案即可

Code

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=1000005;
#define mod 1000000007
char s[maxn];
int n,m,col[maxn],row[maxn],Next[maxn],a[maxn],b[maxn];
P que[maxn];
int ID(int x,int y)
{
    return x*m+y;
}
int deal(int *c,int n)
{
    for(int i=0,j=-1;i<=n;j++,i++)
    {
        Next[i]=j;
        while(j>-1&&c[i]!=c[j])j=Next[j];
    }
    return n-Next[n];
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++)scanf("%s",s+i*m);
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
            scanf("%d",&a[ID(i,j)]);
    int p,q;
    for(int j=0;j<m;j++)
    {
        col[j]=0;
        int temp=1;
        for(int i=0;i<n;i++,temp=26ll*temp%mod)
            col[j]=(col[j]+(ll)temp*(s[ID(i,j)]-'a')%mod)%mod;
    }
    for(int i=0;i<n;i++)
    {
        row[i]=0;
        int temp=1;
        for(int j=0;j<m;j++,temp=26ll*temp%mod)
            row[i]=(row[i]+(ll)temp*(s[ID(i,j)]-'a')%mod)%mod;
    }
    p=deal(row,n),q=deal(col,m);
    for(int i=0;i<n;i++)
    {
        int st=1,ed=0;
        for(int j=0;j<m;j++)
        {
            while(st<=ed&&j-que[st].second>=q)st++;
            while(st<=ed&&a[ID(i,j)]>que[ed].first)ed--;
            que[++ed]=P(a[ID(i,j)],j);
            if(j>=q-1)b[ID(i,j)]=que[st].first; 
        }
    }
    int ans=INF;
    for(int j=q-1;j<m;j++)
    {
        int st=1,ed=0;
        for(int i=0;i<n;i++)
        {
            while(st<=ed&&i-que[st].second>=p)st++;
            while(st<=ed&&b[ID(i,j)]>que[ed].first)ed--;
            que[++ed]=P(b[ID(i,j)],i);
            if(i>=p-1)ans=min(ans,que[st].first);
        }
    }
    printf("%lld\n",(ll)(p+1)*(q+1)*ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值