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

Description

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

Input

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

(0nm106)

Output

输出 B B 的代价最小值

Sample Input

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

Sample Output

18

Solution

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

在求出 p,q p , q 的值之后,显然 A A 的任意一个p×q子矩阵均可生成 C C ,我们要找的是矩阵最大值最小的子矩阵,首先对每行用优先队列求出以(i,j)结尾的长度为 q q 的子段的ai,j最大值 bi,j b i , j ,再对每行用优先队列求出以 (i,j) ( i , j ) 结尾的长度为 p p 的子段的bi,j最大值,这个最大值即为以 (i,j) ( i , j ) 为右下角的 p×q p × 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、付费专栏及课程。

余额充值