【jzoj5045】【无限棋盘】【哈希】

本文介绍了一种在无限重复的棋盘上进行游戏的概率计算方法。通过哈希技术和类RMQ方法,解决了随机选取路径并计算两次路径相同概率的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

无聊的小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

#include<set>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LF double
#define LL long long
#define ULL unsigned long long
#define min(a,b) ((a<b)?a:b)
#define max(a,b) ((a>b)?a:b)
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define fr(i,j) for(int i=Begin[j];i;i=Next[i])
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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值