Description
某日,小 Q 得到了一种新的生成 01 串的代码
给定一个整数 Z,执行 n 次下列语句会得到一个 01 串
z=[(a*z+c)/k]%m;
if (z<m/2) return 0;else return 1;
现在小 Q 已经得到了某串 01 串,小 Q 想知道有多少个可能的不同的最初的 Z 可以生成这个
01 串。
给定一个整数 Z,执行 n 次下列语句会得到一个 01 串
z=[(a*z+c)/k]%m;
if (z<m/2) return 0;else return 1;
现在小 Q 已经得到了某串 01 串,小 Q 想知道有多少个可能的不同的最初的 Z 可以生成这个
01 串。
Input
第一行五个整数 a, c, k, m, n。
第二行 n 个连续的 01 数字描述 01 串。
第二行 n 个连续的 01 数字描述 01 串。
Output
一行一个整数表示答案
Sample Input
3 6 2 9 2 10
Sample Output
4
Data Constraint
对于 30%的数据,1<=n,m<=10^3
对于 60%的数据,1<=n<=10^3
对于 100%的数据,1<=n<=10^5,1<=m<=10^6,0<=a,c<=m,1<=k<=m
对于 60%的数据,1<=n<=10^3
对于 100%的数据,1<=n<=10^5,1<=m<=10^6,0<=a,c<=m,1<=k<=m
题解
- 考虑字符串哈希。
- 设f[i][j]表示i为z的初始值,进行2^j次变化之后,得到的字符串的哈希值
- 设g[i][j]表示i为z的初始值,进行2^j次变化后,得到的数
- 这两个都是可以通过倍增得到,字符串的哈希值可以将两个字符串拼接得到
- 最后,枚举z,倍增判断是否合法
代码
1 #include<cstdio> 2 using namespace std; 3 const int mo=1000000007; 4 int a,c,k,m,n,mx,ans,mi[1000010],f[1000010][17],g[1000010][17]; 5 char s[100010]; 6 int main() 7 { 8 freopen("zero.in","r",stdin); 9 freopen("zero.out","w",stdout); 10 scanf("%d%d%d%d%d",&a,&c,&k,&m,&n); 11 scanf("%s",s+1); 12 mi[0]=2; for (int i=1;i<=16;i++) mi[i]=(long long)mi[i-1]*mi[i-1]%mo; 13 for (int i=1;i<=n;i++) mx=((long long)mx*2+s[i]-'0')%mo; 14 for (int i=0;i<m;i++) 15 { 16 int z=((long long)a*i+c)/k%m; 17 g[i][0]=z,f[i][0]=z<m/2?0:1; 18 } 19 for (int j=1;j<=16;j++) 20 for (int i=0;i<m;i++) 21 { 22 int z=g[i][j-1]; 23 f[i][j]=((long long)f[i][j-1]*mi[j-1]+f[z][j-1])%mo; 24 g[i][j]=g[z][j-1]; 25 } 26 for (int i=0;i<m;i++) 27 { 28 int t=i,l=0; 29 for (int j=n,u=0;j;j>>=1,u++) 30 if (j%2==1) 31 { 32 l=((long long)l*mi[u]+f[t][u])%mo; 33 t=g[t][u]; 34 } 35 if (l==mx) ans++; 36 } 37 printf("%d",ans); 38 return 0; 39 }