[2019寒假集训day4]最佳农场(二维卷积)

题面

在这里插入图片描述

样例输入

5 8
GLGGLGLG
GGLGGLGL
GGLLLGGG
LLGLLGLG
LGGGLGLL
3
2 3
GLL
LGG
3 1
L
G
G
1 4
GGLL

样例输出

1 3
1 2
3 1

数据规模

1 ≤ R , C ≤ 500 , B ≤ 5 1≤R,C≤500,B≤5 1R,C500,B5

题解

看到题解的时候惊了…
二维卷积???
其实也没有那么复杂…
先考虑如何将其转换为一个二维卷积问题。
可以发现,将G用数字0代替,L用数字1代替,那么现在在农场中选取区域的右下端点 ( p x , p y ) (px,py) (px,py),那么同一个位置都是G的个数就是每个位置的两个数字值相乘再相加(L同理),是不是很像卷积?
我们可以从算贡献的角度入手。
可以知道的是:如果在农场和矩阵中分别确定一个点,那么它们一定会给有且只有一个 ( p x , p y ) (px,py) (px,py)做出贡献。
设在农场的大小为 R ∗ C R*C RC,矩阵的大小为 n ∗ m n*m nm,在农场选取的点为 ( x , y ) (x,y) (x,y),在矩阵上选取的点为 ( i , j ) (i,j) (i,j)
[由于蒟蒻电脑画图太丑,请大家自行画图观察一下]
它们所做出贡献的点就是 ( n − i − 1 + x , m − j − 1 + y ) (n-i-1+x,m-j-1+y) (ni1+x,mj1+y)
将其拆成 ( n − i − 1 , m − j − 1 ) + ( x , y ) (n-i-1,m-j-1)+(x,y) (ni1,mj1)+(x,y)
可以看成是矩阵上下左右翻转后与农场进行卷积。
注意到本质上是一个二元组的一维卷积,不妨将其直接变成 ( n − i − 1 ) ∗ R + m − j − 1 (n-i-1)*R+m-j-1 (ni1)R+mj1 x ∗ R + y x*R+y xR+y做一维卷积。
容易发现他们是等价的。
也就是说,将矩阵上下左右翻转后,将每个位置 ( i , j ) (i,j) (i,j)映射到位置 i ∗ R + j i*R+j iR+j,直接进行一维卷积就好。
复杂度: O ( T ∗ R ∗ C log ⁡ R ∗ C ) O(T*R*C\log{R*C}) O(TRClogRC)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
#define MAXN 500
#define MAXM 1000000
#define LL long long
#define MOD 998244353
#define G 3
using namespace std;
int n,m,m1,n1,len,T;
char mp[MAXN+5][MAXN+5];
LL ans;int px,py;
typedef LL Ploy[MAXM+5];
Ploy g,l,g1,l1,res;
LL fst_pow(LL a,LL b)
{
    LL res=1;
    while(b){
        if(b&1)res=(1LL*res*a)%MOD;
        a=(1LL*a*a)%MOD;
        b>>=1;
    }
    return res;
}
void NTT(Ploy &a,int n,int x)
{
    for(int i=0,j=0;i<n;i++)
    {
        if(i<j)swap(a[i],a[j]);
        int k=n>>1;
        while(k&&(k&j))j^=k,k>>=1;
        j^=k;
    }
    for(int i=1;i<n;i<<=1)
    {
        int gn=fst_pow(G,(MOD-1)/(i<<1)),g=1;
        if(x==-1)gn=fst_pow(gn,MOD-2);
        for(int j=0;j<i;j++,g=(1LL*g*gn)%MOD)
            for(int l=j,r=l+i;l<n;l+=(i<<1),r=l+i)
            {
                int tmp=(1LL*a[r]*g)%MOD;
                a[r]=(a[l]-tmp+MOD)%MOD;
                a[l]=(a[l]+tmp)%MOD;
            }
    }
    if(x==1)return ;
    int ny=fst_pow(n,MOD-2);
    for(int i=0;i<n;i++)a[i]=(1LL*a[i]*ny)%MOD;
}
void Cg(Ploy &a,Ploy &b)
{
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
		a[i*m+j]=(mp[i][j]=='G');
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
		b[i*m+j]=(mp[i][j]=='L');
	NTT(a,len,1),NTT(b,len,1);
}
void cal()
{
	memset(res,0,sizeof(res));
	for(int i=0;i<len;i++)
		res[i]=(1LL*g[i]*g1[i]%MOD+1LL*l[i]*l1[i]%MOD)%MOD;
	NTT(res,len,-1);
}
int main()
{
	freopen("best.in","r",stdin);
	freopen("best.out","w",stdout);
	scanf("%d%d",&n,&m);
	len=1;
	while(len<=n*m*2)len<<=1;
	for(int i=0;i<n;i++)scanf("%s",mp[i]);
	Cg(g,l);
	scanf("%d",&T);
	while(T--)
	{
		memset(mp,0,sizeof(mp));
		memset(l1,0,sizeof(l1));
		memset(g1,0,sizeof(g1));
		scanf("%d%d",&n1,&m1);
		for(int i=0;i<n1;i++)scanf("%s",mp[i]);
		for(int i=0;i<n1;i++)reverse(mp[i],mp[i]+m1);
		for(int i=0;i<n1&&i<n1-i-1;i++)swap(mp[i],mp[n1-i-1]);
		Cg(g1,l1);
		cal();
		ans=0;px=-1,py=-1;
		for(int i=n1-1;i<n;i++)
			for(int j=m1-1;j<m;j++)
			if(px==-1||ans<res[i*m+j]){
				ans=res[i*m+j];
				px=i-n1+2,py=j-m1+2;
			}
		printf("%d %d\n",px,py);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值