题目大意
无聊的小A在一个无限大的棋盘上玩游戏,这个棋盘由一个M*N的模板不停重复生成。例如,当模板为:
honi
hsin
时,我们会生成如下棋盘:
…honihonihonihoni…
…hsinhsinhsinhsin…
…honihonihonihoni…
…hsinhsinhsinhsin…
其中,该棋盘在任意一个方向都可以无限延伸。
现在小A在棋盘上随机挑选一个位置,又随机挑选一个方向(八个方向之一),并从该位置开始,沿着挑选的方向走K-1步,沿路记下每一个经过的字母(包括起点),得到一个长度为K的字符串。他重复并独立地执行该操作两次,得到两个长度为K的字符串,他现在想知道,这两个字符串相同的概率是多大?
解题思路
考虑哈希,f[l][k][i][j]表示从(i,j)开始往k方向走2^l步的哈希值,可以用类rmq(st表)的方法求。
哈希时用类rmq的方法,用最大的前后两段共同表示表示即可。注意使用滚动数组。
code
using namespace std;
int const mn=500+1,mlgk=31,size=2*1e6,Inf=1e9;
int n,m,K,pow2[mlgk],cnt[size+9],w[8][2]={{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};
ULL hs1[size+9],hs2[size+9],f[2][8][mn][mn],pow31[mlgk];
LL gc(LL x,LL y){
LL z;
while(z=x%y){
x=y;
y=z;
}
return y;
}
int hash(ULL x,ULL y){
int p=x%size;
while(hs1[p]&&((hs1[p]!=x)||(hs2[p]!=y)))p=(p+1==size)?0:p+1;
return p;
}
int main(){
//freopen("chessboard.in","r",stdin);
//freopen("chessboard.out","w",stdout);
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
scanf("%d%d%d\n",&m,&n,&K);
char ch;
fo(i,1,m){
fo(j,1,n){
scanf("%c",&ch);
fo(k,0,7)f[0][k][i][j]=ch-'a'+1;
}
scanf("\n");
}
int log2=log(K)/log(2),ii,jj,tm2,ll;ULL tmp;
pow2[0]=1;pow31[0]=31;
fo(i,1,log2)pow2[i]=pow2[i-1]*2,pow31[i]=pow31[i-1]*pow31[i-1];
fo(l,1,log2)fo(k,0,7)fo(i,1,m)fo(j,1,n){
ll=l&1;ii=((i+w[k][0]*pow2[l-1]-1)%m+m)%m+1;
jj=((j+w[k][1]*pow2[l-1]-1)%n+n)%n+1;
f[ll][k][i][j]=f[ll^1][k][i][j]+f[ll^1][k][ii][jj]*pow31[l-1];
}
ll=log2&1;
fo(k,0,7)fo(i,1,m)fo(j,1,n){
ii=((i+w[k][0]*(K-pow2[log2])-1)%m+m)%m+1;
jj=((j+w[k][1]*(K-pow2[log2])-1)%n+n)%n+1;
tm2=hash(f[ll][k][i][j],f[ll][k][ii][jj]);
hs1[tm2]=f[ll][k][i][j];
hs2[tm2]=f[ll][k][ii][jj];
cnt[tm2]++;
}
LL ax=0,ay=1ll*n*n*m*m*64;
fo(i,0,size-1)if(cnt[i])
ax+=1ll*cnt[i]*cnt[i];
LL gcd=gc(ax,ay);
ax/=gcd;ay/=gcd;
printf("%lld/%lld",ax,ay);
return 0;
}