【JZOJ5045】【NOI2017模拟4.5】无限棋盘

55 篇文章 0 订阅
15 篇文章 0 订阅

Description

无聊的小A在一个无限大的棋盘上玩游戏,这个棋盘由一个M*N的模板不停重复生成。例如,当模板为:
honi
hsin
时,我们会生成如下棋盘:
…honihonihonihoni…
…hsinhsinhsinhsin…
…honihonihonihoni…
…hsinhsinhsinhsin…
其中,该棋盘在任意一个方向都可以无限延伸。
现在小A在棋盘上随机挑选一个位置,又随机挑选一个方向(八个方向之一),并从该位置开始,沿着挑选的方向走K-1步,沿路记下每一个经过的字母(包括起点),得到一个长度为K的字符串。他重复并独立地执行该操作两次,得到两个长度为K的字符串,他现在想知道,这两个字符串相同的概率是多大?

Data Constraint

62.5%的数据满足:M=N。

Solution

我们发现这k步走的都是一个方向,所以经大佬提醒,我们这k步可以用倍增来做。我们设f[i][j][k][l]表示以(i,j)为起点往l方向走2^k步后的字符串的哈希值。这显然可以用倍增来做。然后我们把所有起点的八方向的走k步后的字符串的哈希值都加入数组。排一下序就可找出所有相同的字符串的数量。与64*(n*m)^2取一下gcd即可。注意:哈希值这种东西可以用unsigned long long 的2^64自然溢出来做。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm> 
#define ll long long
using namespace std;
const ll maxn=500+5,mo=1e9+3;
const ll f[8][2]={{0,1},{1,0},{0,-1},{-1,0},{1,1},{-1,1},{1,-1},{-1,-1}};
ll f1[maxn][maxn][30],er[maxn],a[maxn*maxn*8];
char s[maxn];
ll n,m,p,i,t,j,k,l,x,y,z,ans,q,num;
ll gcd(ll x,ll y){
    ll r=x%y;
    while (r) x=y,y=r,r=x%y;
    return y;
}
int main(){
    freopen("chessboard.in","r",stdin);freopen("chessboard.out","w",stdout);
    scanf("%llu%llu%llu\n",&n,&m,&p);
    for (i=1;i<=n;i++){
        scanf("%s\n",s+1);
        for (j=1;j<=m;j++){
            t=s[j]-97;
            f1[i][j][0]=t;
        }
    }
    t=log(p)/log(2);er[0]=1;
    for (i=1;i<=t;i++)
        er[i]=er[i-1]*2;
    for (l=0;l<8;l++){z=27;
        for (k=1;k<=t;k++){
            for (i=1;i<=n;i++)
                for (j=1;j<=m;j++){
                    x=(i+f[l][0]*er[k-1])%n;y=(j+f[l][1]*er[k-1])%m;
                    x=(x<=0)?x+n:x;y=(y<=0)?y+m:y;
                    f1[i][j][k]=f1[i][j][k-1]*z+f1[x][y][k-1];
                }
            z=z*z;
        }
        for (i=1;i<=n;i++)
            for (j=1;j<=m;j++){
                x=i,y=j;q=0;k=27;
                for (z=0;z<=t;z++){
                    if (er[z]&p){
                        q=q*k+f1[x][y][z];
                        x=(x+f[l][0]*er[z])%n;y=(y+f[l][1]*er[z])%m;
                        x=(x<=0)?x+n:x;y=(y<=0)?y+m:y;
                    }
                    k=k*k;
                }
                a[++num]=q;
            }
    }
    sort(a+1,a+num+1);t=0;
    for (i=1;i<=num;i++)
        if (a[i]==a[i-1]) t++;
        else ans+=t*t,t=1;
    ans+=t*t;
    p=64*n*n*m*m;
    t=gcd(ans,p);
    ans/=t;p/=t;
    printf("%llu/%llu\n",ans,p);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值