nowcoder16638 carpet

链接

点击跳转

题解

首先注意到,如果一个子矩阵可以作为循环节,那么如果另一个能作为循环节的矩阵包含了这个矩阵,那么大一点的矩阵的答案一定不会比小矩阵更优。

所以,我只需要找最小循环节。

先把每行求出一个哈希值,然后用这个哈希值做kmp,这样就得到了行方向上的最小循环节长度,假设是p

然后把前p行拿出来,每一列求一个哈希值,再kmp。这样得到一个列方向上的最小循环节

然后现在就相当于拿一个大小为p*q的框,去框一个子矩阵,使得最大值最小

这个东西在两个方向上维护单调队列就可以做

代码

#include <bits/stdc++.h>
#define iinf 0x3f3f3f3f
#define linf (1ll<<60)
#define eps 1e-8
#define maxn 2000010
#define cl(x) memset(x,0,sizeof(x))
#define rep(i,a,b) for(i=a;i<=b;i++)
#define drep(i,a,b) for(i=a;i>=b;i--)
#define em(x) emplace(x)
#define emb(x) emplace_back(x)
#define emf(x) emplace_front(x)
#define fi first
#define se second
#define de(x) cerr<<#x<<" = "<<x<<endl
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
ll read(ll x=0)
{
    ll c, f(1);
    for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
    for(;isdigit(c);c=getchar())x=x*10+c-0x30;
    return f*x;
}
ll n, m;
int Hash[maxn];
struct Array2
{
    ll n, m, a[maxn];
    void init(ll N, ll M)
    {
        n=N, m=M;
        for(ll i=0;i<n*m;i++)a[i]=0;
    }
    ll index(ll i, ll j){return i*m+j;}
    ll& at(ll i, ll j){return a[index(i,j)];}
    ll* operator[](ll i){return a+i*m;}
}w, s, dq;
ll L[maxn], R[maxn];
struct KMP
{
    int n, next[maxn], t[maxn];
    void build(int *r, int len)
    {
        int i, j=0;
        n=len;
        for(i=1;i<=len;i++)t[i]=r[i];  t[len+1]=0;
        for(i=2;i<=len;i++)
        {
            for(;j and t[j+1]!=t[i];j=next[j]);
            next[i] = t[j+1]==t[i]?++j:0;
        }
    }
    int move(int pos, int x)
    {
        for(;pos and t[pos+1]!=x;pos=next[pos]);
        return t[pos+1]==x ? pos+1 : 0;
    }
}kmp;
char str[maxn], tmp[maxn];
ll gethash(char *s, ll len)
{
    ll i, ret=0, base=4894111, mod=998244353;
    rep(i,1,len)ret = (ret * base + s[i]) %mod;
    return ret;
}
int main()
{
    de( ( sizeof(w)+sizeof(s)+sizeof(dq) >>20 ) );
    ll i, j;
    n=read(), m=read();
    w.init(n+1,m+1), s.init(n+1,m+1), dq.init(n+1,m+1);
    rep(i,1,n)
    {
        scanf("%s",str+1);
        rep(j,1,m)s[i][j]=str[j];
        Hash[i]=gethash(str,m);
    }
    rep(i,1,n)rep(j,1,m)w[i][j]=read();
    kmp.build(Hash,n);
    ll p = n - kmp.next[n];
    rep(j,1,m)
    {
        rep(i,1,p)tmp[i]=s[i][j];
        Hash[j] = gethash(tmp,p);
    }
    kmp.build(Hash,m);
    ll q = m - kmp.next[m], ans=linf;
    rep(j,1,m)
    {
        rep(i,1,n)
        {
            auto que = dq[i];
            auto &l=L[i], &r=R[i];
            if(l<r and que[l]<j-q+1)l++;
            while(l<r and w[i][que[r-1]] < w[i][j])r--;
            que[r++] = j;
        }
        deque<int> que;
        rep(i,1,n)
        {
            if(que.size() and que.front()<i-p+1)que.pop_front();
            while(que.size() and w[que.back()][ dq[que.back()][L[que.back()]] ] < w[i][ dq[i][L[i]] ])que.pop_back();
            que.push_back(i);
            if(i>=p and j>=q)
            {
                ans = min(ans,w[que.front()][ dq[que.front()][L[que.front()]] ]);
            }
        }
    }
    printf("%lld",ans*(p+1)*(q+1));
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值