JZOJA组 01串

Description

某日,小Q得到了一种新的生成 01 串的代码。
给定一个整数 Z,执行 n 次下列语句会得到一个 01 串
z=[(a*z+c)/k]%m;
if (z现在小Q已经得到了某串01串,小Q想知道有多少个可能的不同的最初的 Z 可以生成这个01串。

Input

第一行五个整数 a, c, k, m, n。
第二行 n 个连续的 01 数字描述 01 串。

 

Output

一行一个整数表示答案。

Sample Input

3 6 2 9 2 10

Sample Output

4

Hint

【数据规模与约定】
对于 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,0<=z< m

好题,倍增+哈希。

维护g[i][j]表示第i个数经过2^j操作过后变成的数,

维护f[i][j]表示i经过2^j变换过后的哈希值,方程手推一下就出来了。

值得注意的是JZOJ这道题卡常,双哈希被卡,大数组放前面被卡,被卡成了暴力分= =

#include<bits/stdc++.h>
using namespace std;
const int e1=31,p1=1e9+7;
const int Maxn=100005,Maxm=1000005;
char s[Maxn];
int a,c,k,m,n,len,goal1;
int f[17][Maxm];
int h1[17][Maxm];
int f1[Maxn]={1};
inline void ST(){
	for(int i=1;i<=1e5;++i)f1[i]=1ll*f1[i-1]*e1%p1;
	for(int i=0;i<m;++i){
		f[0][i]=((1ll*a*i+c)/k)%m;
		h1[0][i]=(f[0][i]>=m/2);
	}
	int maxlog=log2(n);
	for(int j=1;j<=maxlog;++j)
		for(int i=0;i<m;++i){
			f[j][i]=f[j-1][f[j-1][i]];
			h1[j][i]=(1ll*h1[j-1][i]*f1[(1<<j-1)]+h1[j-1][f[j-1][i]])%p1;
		}
}
inline bool check(int st,int x){
	int st1=0,st2=0,maxlog=log2(x);
	for(int i=maxlog;i>=0;--i)if(x-(1<<i)>=0){
		x-=(1<<i);
		st1=(1ll*st1*f1[1<<i]+h1[i][st])%p1;
		st=f[i][st];
	}
	return st1==goal1;
}
int main(){
//	freopen("zero.in","r",stdin);
//	freopen("zero.out","w",stdout);
	cin>>a>>c>>k>>m>>n;
	scanf("%s",s+1);
	len=strlen(s+1);
	for(int i=1;i<=len;++i){
		goal1=(1ll*goal1*e1+(s[i]-'0'))%p1;
	}
	ST();
	int ret=0;
	for(int i=0;i<m;++i)ret+=check(i,n);
	printf("%d\n",ret);
	return 0;
}
/*
3 6 2 9 2
10
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值