题目大意
给你一个只有0,1,2的n*n的矩阵,你现在可以选择一个叉叉或者十字,把里面的数全部取掉,贡献为取掉的数的乘积。
具体的,从某一点(i,j)出发,选择一个长度c:
你可以这样取(1为取,0为不取)
00100
00100
11111
00100
00100
或者这样取
10001
01010
00100
01010
10001
n<=1000
分析
很简单的思路,弄两种,共4个前缀和数组,维护两个对角线的前缀和,还有一行一列的前缀和,然后枚举以哪个点作为中心。然后怎么确定这个点怎么取呢?我们知道如果要取,在取不到0时,肯定c越大越好;取到0就没有意义了。所以两种取法,分别二分长度,看看最大的c是多少,用log比较大小,更新答案即可。
其实原来我还怕精度不够,想直接弄double比较,然而精度其实差不多,而且double只有301位,即
21000
,如果全是3的话早爆了。
这道题比较麻烦的地方是对角线前缀和数组,要码得快必须要先规定好表示法,然后再打,这样才能思路清晰,不至于打得慢。
其实这题最好用直角坐标系,然后直接设数组bx[i][j]表示y=±x+i这条直线,会比较好做。
然而我没有转成坐标系,直接bx[i][j]表示起点为(i,1),到(i+j-1,j)的前缀和。另一条对角线类似,以(i,n)为起点,为了储存地址都是正数,还要整体移n。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef double db;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
const int N=2005;
const ll mo=1e9+7;
struct rec
{
int c[4];
}ax[N][N],ay[N][N],bx[N][N],by[N][N],ret,ans;
int i,j,k,n,m,l,r,mid,a[N][N],xx,x,y,mn,z,prt;
db two[N];
char ch;
rec ins(rec a,int z)
{
a.c[z]++;
return a;
}
bool operator <(rec a,rec b)
{
if (a.c[0]) return 1;
if (b.c[0]) return 0;
db aa,bb;
aa=log(2)*(db)a.c[2]+log(3)*(db)a.c[3];
bb=log(2)*(db)b.c[2]+log(3)*(db)b.c[3];
return aa<bb;
}
rec operator +(rec a,rec b)
{
a.c[0]=a.c[0]+b.c[0];
a.c[1]=a.c[1]+b.c[1];
a.c[2]=a.c[2]+b.c[2];
a.c[3]=a.c[3]+b.c[3];
return a;
}
rec operator -(rec a,rec b)
{
a.c[0]=a.c[0]-b.c[0];
a.c[1]=a.c[1]-b.c[1];
a.c[2]=a.c[2]-b.c[2];
a.c[3]=a.c[3]-b.c[3];
return a;
}
void get1(int x)
{
ret=ax[i][j+x]-ax[i][j-x-1]+ay[j][i+x]-ay[j][i-x-1];
}
void get2(int x)
{
int y=n-j+1;
ret=bx[i-j+n+1][j+x]-bx[i-j+n+1][j-x-1]+by[i+j][y+x]-by[i+j][y-x-1];
}
int main()
{
// freopen("677.in","r",stdin);
scanf("%d\n",&n);
fo(i,1,n)
{
fo(j,1,n)
{
scanf("%c",&ch);
a[i][j]=ch-'0';
}
scanf("\n");
}
ans.c[0]=1;
fo(i,1,n)
fo(j,1,n)
{
ax[i][j]=ins(ax[i][j-1],a[i][j]);
ay[j][i]=ins(ay[j][i-1],a[i][j]);
}
fo(xx,-n+2,n)
{
i=xx+n;
x=xx;
z=n;
fo(y,1,n)
{
if (x>0)
{
bx[i][y]=ins(bx[i][y-1],a[x][y]);
by[i][y]=ins(by[i][y-1],a[x][z]);
}
x++;
z--;
}
}
fo(i,1,n)
fo(j,1,n)
if (a[i][j])
{
// non-rotated
mn=min(min(i-1,n-i),min(j-1,n-j));
l=0;
r=mn;
while (l<r)
{
mid=(l+r+1)/2;
get1(mid);
if (ret.c[0]==0) l=mid;
else r=mid-1;
}
get1(l);
ret.c[a[i][j]]--;
if (ans<ret)
ans=ret;
//rotated
l=0;
r=mn;
while (l<r)
{
mid=(l+r+1)/2;
get2(mid);
if (ret.c[0]==0) l=mid;
else r=mid-1;
}
get2(l);
ret.c[a[i][j]]--;
if (ans<ret)
ans=ret;
}
prt=1;
fo(i,0,3)
fo(j,1,ans.c[i])
prt=(ll)prt*i%mo;
printf("%d\n",prt);
}