1 条题解
-
0
自动搬运
来自洛谷,原作者为

囧仙
你做东方鬼畜音MAD,好吗?搬运于
2025-08-24 22:00:32,当前版本为作者最后更新于2021-01-24 12:40:08,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题目大意
给一个由 的字符矩阵无限复制得到的矩阵。等概率随机选择一个起点,并等概率随机选择一个方向(上、下、左、右、左上、左下、右上、右下),将这个方向上 个字符组成一个字符串。询问这样选出来的两个字符串相同的概率。
。
题解
我们设 表示第 行第 列的字符。由无限复制的定义可以得到, 。
考虑计算出每个起点、每个方向上组成的字符串的哈希值。这里我们采用如下哈希方式:
$$H(X)=\sum_{i=0}^{len} X_iP^{len-i-1} \pmod{2^{64}} $$其中 是一个长度为 ,下标范围是 的字符串。
如果直接暴力计算所有的字符串,这样的时间复杂度是 ,显然是不行的。但事实上,我们可以找到 的循环节。可以证明, 的循环节长度不会超过 (考虑 的下标的模意义)。
假定有一个循环节是 的字符串,它的长度为 。
我们可以计算出它循环节的哈希值(设为 ,它的长度为 ),那么完整的循环部分的哈希值应该为:
$$\begin{aligned}H(\verb!aabaabaab!)&=H_0\times \left(P^3\right)^2+H_0\times \left(P^3\right)^1+H_0\times \left(P^3\right)^0\cr &=H_0\left(\left(P^3\right)^2+\left(P^3\right)^1+\left(P^3\right)^0\right) \end{aligned}$$显然,括号里的是一个首项为 ,公比为 的等比数列。对于一个等比数列,我们有如下公式:
但是由于模数是 ,而 是不存在乘法逆元的。考虑使用分治法。
假设我们要计算 ,那么有:
$$F(q,n)=\begin{cases} 0 & n=0 \cr 1 & n=1 \cr (1+q)\times F(q^2,\lfloor n\div2\rfloor) & n\equiv 0\pmod 2 \cr (1+q)\times F(q^2,\lfloor n\div2\rfloor)\times q+1 & n\equiv 1\pmod 2 \cr \end{cases}$$这么做的复杂度是 。
对于 的后半部分,我们可以直接暴力。这样的复杂度不超过 。但由于 可能达到 ,于是总复杂度为 。显然,我们还要进一步优化。
不妨以样例二为例:
$$\begin{bmatrix} \tt\color{grey}\textcolor{black}{b\ a\ n}\ b\ a\ n\ b\ a\ n\ b\ a\ n \cr \tt\color{grey}\textcolor{black}{a}\ \underlinesegment{\textcolor{black}{n\ a}\ a\ n\ a\ a\ n\ a\ a\ n}\ a \cr \tt\color{grey}n\ a\ b\ n\ a\ b\ n\ a\ b\ b\ a\ n \cr \tt\color{grey}b\ a\ n\ b\ a\ n\ b\ a\ n\ a\ n\ a \cr \tt\color{grey}a\ n\ a\ a\ n\ a\ a\ n\ a\ b\ a\ n \cr \tt\color{grey}n\ a\ b\ n\ a\ b\ n\ a\ b\ a\ n\ a \cr \end{bmatrix}$$假如我们已经计算出了 这一段的哈希值,现在要计算它右边的一个字符串 。我们能够发现,它们共用一个循环节。它的哈希值就是把 乘上 、减去最左侧的字母 的贡献、再加上最右侧 的贡献。
也就是说,只要我们计算出任意一个长度为 的字符串的哈希值,就能 转移到将它向右移动一位的字符串的哈希值。但事实上,我们可以预处理 ,那么单次转移的复杂度就降为了 。
计算一个长度为 的字符串的哈希值的复杂度是 ,我们再花费 的复杂度将共用这个循环节的其他的长度为 的字符串的哈希值求出来。于是平均每个字符串的复杂度是 。又因为一共只有 个需要计算的字符串,于是总复杂度为 ,可以通过本题。
另外,我们需要开一个哈希表将哈希值相同的字符串插入进去,然后统计相同哈希值的字符串有多少个,计算出共有多少种选法使得两个字符串哈希值相同。除以总方案数 就行了。
实测跑的飞起(
参考代码
#include<bits/stdc++.h> #define up(l,r,i) for(int i=l,END##i=r;i<=END##i;++i) #define dn(r,l,i) for(int i=r,END##i=l;i>=END##i;--i) using namespace std; typedef long long i64; typedef unsigned int u32; typedef unsigned long long u64; const int INF =2147483647; int qread(){ int w=1,c,ret; while((c=getchar())> '9'||c< '0') w=(c=='-'?-1:1); ret=c-'0'; while((c=getchar())>='0'&&c<='9') ret=ret*10+c-'0'; return ret*w; } const int MAXN=500+3,P=13331; char S[MAXN][MAXN],T[MAXN*MAXN]; u64 h; bool vis[MAXN][MAXN]; int n,m,p,s,t; i64 ans1,ans2; const int dir[8][2]={{1,0},{0,1},{-1,0},{0,-1},{1,1},{1,-1},{-1,1},{-1,-1}}; u64 pwr(u64 a,u64 b){ u64 r=1; while(b){if(b&1) r=r*a; a=a*a,b>>=1;} return r; } const int SIZ =999997,MAXM=MAXN*MAXN*8; int head[SIZ],val[MAXM],nxt[MAXM],tot; u64 ver[MAXM]; void add(int u,u64 v){ ver[++tot]=v,nxt[tot]=head[u],val[tot]=1,head[u]=tot; } void inc(u64 h){ for(int p=head[h%SIZ];p;p=nxt[p]) if(ver[p]==h){ans1+=2ll*val[p]+1,++val[p];return;} add(h%SIZ,h),++ans1; } u64 calc(u64 a,u64 b){ //calc a^0+a^1+a^2+...+a^(b-1) if(b==0) return 0; if(b==1) return 1; if(b&1) return (1ull+a)*calc(a*a,b>>1)*a+1; else return (1ull+a)*calc(a*a,b>>1); } int main(){ n=qread(),m=qread(),p=qread(); u64 q=pwr(P,p); up(0,n-1,i) scanf("%s",S[i]); up(0,7,d){ up(0,n-1,i) up(0,m-1,j) if(!vis[i][j]){ int a=i,b=j; h=0; while(!vis[a][b]){ h=h*P+S[a][b],T[t++]=S[a][b],vis[a][b]=true; a=(n+a+dir[d][0])%n,b=(m+b+dir[d][1])%m; } int x=p%t; h*=calc(pwr(P,t),p/t); up(0,x-1,k) h=h*P+T[k]; up(0,t-1,k) inc(h),h=h*P+T[(x+k)%t]-T[k]*q; t=0; } up(0,n-1,i) up(0,m-1,j) vis[i][j]=0; } ans2=1ll*n*m*8*(1ll*n*m*8); i64 d=__gcd(ans1,ans2); printf("%lld/%lld\n",ans1/d,ans2/d); return 0; }
- 1
信息
- ID
- 3439
- 时间
- 4000ms
- 内存
- 250MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者