题目大意
给定一个n行
现在你要在这个棋盘上随机挑选一个位置,又随机挑选一个方向(八个方向之一),并从该位置开始,沿着挑选的方向走K−1步,沿路记下每一个经过的字母(包括起点),得到一长度为K的字符串。试求出重复并独立执行该操作两次,得到的两个长度为
以最简分数形式输出答案。
1≤n,m≤500,2≤K≤109
题目分析
基本思路是弄出n×m个格子所有方向的哈希值都弄出来,然后排序计算。
但是怎么搞出那个哈希值才能既保证复杂度又节省代码呢?
倍增!处理2i长度的哈希值合并起来。
时间复杂度O(nmlogK)。
代码实现
#include <algorithm>
#include <iostream>
#include <utility>
#include <cstdio>
using namespace std;
typedef long long LL;
LL sqr(LL x){return x*x;}
const int N=505;
const int M=505;
const int D=8;
const int MOD=199999279;
const int P1=89;
const int P2=461;
typedef pair<int,int> P;
#define mkp(a,b) make_pair(a,b)
#define ft first
#define sd second
P operator+(P a,P b){return mkp((a.ft+b.ft)%MOD,(a.sd+b.sd)%MOD);}
P operator-(P a,P b){return mkp((a.ft-b.ft+MOD)%MOD,(a.sd-b.sd+MOD)%MOD);}
P operator*(P a,P b){return mkp(1ll*a.ft*b.ft%MOD,1ll*a.sd*b.sd%MOD);}
P operator*(P x,int y){return x*mkp(y%MOD,y%MOD);}
P operator+(P x,int y){return x+mkp(y%MOD,y%MOD);}
P operator-(P x,int y){return x-mkp(y%MOD,y%MOD);}
P quick_power(P x,int y)
{
P ret=mkp(1,1);
for (;y;y>>=1,x=x*x) if (y&1) ret=ret*x;
return ret;
}
LL gcd(LL x,LL y){return y?gcd(y,x%y):x;}
int dir[D][2]={{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1}};
P f[2][N][M][D],g[N][M][D];
int tot,n,m,K;
char s[N][M];
P app[N*M*D];
P pri;
LL A,B;
void calc()
{
pri=mkp(P1,P2);
for (int i=0;i<n;++i)
for (int j=0;j<m;++j)
for (int d=0;d<D;++d)
f[0][i][j][d]=mkp(s[i][j]-'a',s[i][j]-'a');
for (int l=0,now=0,lst=1,sig=0;l<30;++l)
{
P tmp;int len=1<<l;
if ((K>>l)&1)
{
tmp=quick_power(pri,sig);
for (int i=0;i<n;++i)
for (int j=0;j<m;++j)
for (int d=0;d<D;++d)
{
int x=((i+dir[d][0]*sig)%n+n)%n,y=((j+dir[d][1]*sig)%m+m)%m;
g[i][j][d]=g[i][j][d]+(f[now][x][y][d]*tmp);
}
sig+=len;
}
tmp=quick_power(pri,len);
swap(now,lst);
for (int i=0;i<n;++i)
for (int j=0;j<m;++j)
for (int d=0;d<D;++d)
{
int x=((i+dir[d][0]*len)%n+n)%n,y=((j+dir[d][1]*len)%m+m)%m;
f[now][i][j][d]=f[lst][i][j][d]+(f[lst][x][y][d]*tmp);
}
}
for (int i=0;i<n;++i)
for (int j=0;j<m;++j)
for (int d=0;d<D;++d)
app[++tot]=g[i][j][d];
sort(app+1,app+1+tot);
for (int i=1,j;i<=tot;i=j)
{
for (j=i+1;j<=tot&&app[i]==app[j];++j);
A+=sqr(j-i);
}
}
int main()
{
freopen("chessboard.in","r",stdin),freopen("chessboard.out","w",stdout);
scanf("%d%d%d",&n,&m,&K);
for (int i=0;i<n;++i) scanf("%s",s[i]);
calc(),B=sqr(1ll*n*m*D);
LL C=gcd(A,B);A/=C,B/=C;
printf("%lld/%lld\n",A,B);
fclose(stdin),fclose(stdout);
return 0;
}