CC LUCKYDAY : Lucky Days 题解

CC LUCKYDAY : Lucky Days 题解

算法标签:BSGS,矩阵

首先我们可以写出转移矩阵(转移可以看作一个横向量乘上一个列向量):

\[0,y,0\\ 1,x,0\\ 0,1,1\\ \]

很明显,它的det=y。

所以我们可以先特判掉y=0的case,这只需要分类讨论即可。

然后可以发现循环节一定\(\leq p^2\)的。我们要找到循环节\(t\)

也就是说找到最小的\(t\)满足:

\[M^t=I\mod p \]

这可以使用BSGS:

\(t=p*A-B,(0\leq A,B\leq B)\),则\((M^p)^A=M^B\)。用哈希之类的就可以了,时间复杂度为\(O(p)\)

找到循环节\(t\)之后,我们要直到每一个横向量一个周期内出现了多少次。

\(h=t^{1.5}\)

先暴力算出\(V\)分别和\(M^0,M^1....M^{h-1}\)的取值。

然后,我们对于每一个\(V_i=(C,i,Z)\)查找是否在\(M^0\)\(M^{k-1}\)中出现过。

如果没有出现我们还需要算出\(M^h.....M^{t-1}\)的结果。

我们可以以\(M^{h-1}\)为一个周期。

则查询\(V_i\)是否可以表示成\(V\times M^j\times M^{i*(h-1)},(0\leq j<h)\)

我们可以枚举\(i\),然后查询\(j\)

我们可以同时在两边乘上\(M^{-(h-1)*i}\)(由于\(y\neq 0\),则一定存在逆矩阵)

放上我TLE的垃圾代码(这题卡常数非常恶心,我也不想调了)

/*
{
######################
#       Author       #
#        Gary        #
#        2021        #
######################
*/
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
//inline int read(){
//    int x=0;
//    char ch=getchar();
//    while(ch<'0'||ch>'9'){
//        ch=getchar();
//    }
//    while(ch>='0'&&ch<='9'){
//        x=(x<<1)+(x<<3)+(ch^48);
//        ch=getchar();
//    }
//    return x;
//}
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
int T;
int a,b,x,y,z,MOD,c,q;
LL quick(LL A,LL B){
	if(B==0) return 1;
	LL  tmp=quick(A,B>>1);
	tmp*=tmp;
	tmp%=MOD;
	if(B&1)
		tmp*=A,tmp%=MOD;
 	return tmp;
}
int inv(int A){
	return quick(A,MOD-2);
}
struct MAT{
	int mat[3][3];
	MAT (){
		memset(mat,0,sizeof(mat));
	}
	MAT I (){
		MAT ret;
		rep(i,3) ret.mat[i][i]=1;
		return ret;
	}
	MAT operator * (MAT oth){
		MAT ret;
		rep(i,3)
			rep(j,3)
				rep(k,3){
					(ret.mat[i][j]+=1ll*mat[i][k]*oth.mat[k][j]%MOD)%=MOD;	
				}
		return ret;
	}
	MAT ksm(int x){
		if(x==0) return I();
		MAT ret=ksm(x>>1);
		ret=ret*ret;
		if(x&1) ret=ret*(*this); 
		return ret;
	}
	MAT getinv(){
		int m[3][6]={0};
		rep(i,3) 
			rep(j,3) m[i][j]=mat[i][j];
		rep(i,3) m[i][3+i]=1;
		MAT ret;
		rep(i,3){
			int z=0;
			for(int j=i;j<3;++j){
				if(m[j][i]){
					z=j;
					break;
				}
			}
			swap(m[z],m[i]);
			int inv_=inv(m[i][i]);
			rep(j,6) m[i][j]=1ll*m[i][j]*inv_%MOD;
			rep(k,3)
				if(k!=i)
				{
					int old=m[k][i];
					rep(j,6)
						(m[k][j]+=MOD-1ll*old*m[i][j]%MOD)%=MOD;
				}
		}
		rep(i,3) rep(j,3) ret.mat[i][j]=m[i][j+3];
		return ret;
	}
	MAT operator ^ (int x){
		return ksm(x);
	}
	bool equal_to_I(){
		rep(i,3) 
			rep(j,3)
				if(i==j){
					if(mat[i][j]!=1) return false;	
				}
				else{
					if(mat[i][j]!=0) return false;
				}
		return true;
	}
};
unordered_map<LL,int> um;
struct VEC{
	int v[3];
	VEC (){memset(v,0,sizeof(v));}
	VEC (int a,int b,int c){
		v[0]=a;
		v[1]=b;
		v[2]=c;
	}
	VEC operator * (MAT m){
		VEC ret;
		rep(i,3)
			rep(j,3)
				(ret.v[j]+=1ll*v[i]*m.mat[i][j]%MOD)%=MOD;
		return ret;
	}
	LL vechash(){
		return (LL)(1e10)*v[0]+(LL)(1e5)*v[1]+v[2];
	}
};
vector<int> pos;
int len=0;
int cl,cr;
LL get(LL bound){
	if(bound==0) return 0;
	if(cl==3){
		LL ret=0;
		if(a==c) ret++;
		if(bound>=2)
		if(b==c) ret++;
		if(bound>=3)
		if(z==c) ret+=(bound-2); 
		return ret;
	}
	if(cl==2){
		LL ret=0;
		if(a==c) ret++;
		bound--;
		ret+=(bound/len)*pos.size();
		bound%=len;
		ret+=upper_bound(ALL(pos),bound)-pos.begin();
		return ret;
	}
	LL ret=0;
	ret+=(bound/len)*pos.size();
	bound%=len;
	ret+=upper_bound(ALL(pos),bound)-pos.begin();
	return ret;
}
int app[10007];
void solve(){
	memset(app,0,sizeof(app));
	cin>>a>>b>>x>>y>>z>>MOD>>c>>q;
	int p=MOD;
	um.clear();
	pos.clear();
	cl=cr=0;
	if(y==0){
		if(x==0){
			cl=cr=3;
		}
		else{
			cl=2;
			cr=2;
			if(b==c) pos.PB(1);
			int now=(x*b+z)%MOD;
			while(now!=b){
				cr++;
				if(now==c){
					pos.PB(cr-1);
				}
				now=(x*now+z)%MOD;
			}
			len=cr-cl+1;
		}
	}
	else{
		VEC V(a,b,z);
		MAT M;
		M.mat[0][0]=0,M.mat[0][1]=y,M.mat[0][2]=0;
		M.mat[1][0]=1,M.mat[1][1]=x,M.mat[1][2]=0;
		M.mat[2][0]=0,M.mat[2][1]=1,M.mat[2][2]=1;
		int h=700000;
		VEC match[10007];
		rep(i,p) match[i]=VEC(c,i,z);
		VEC now=V;
		len=1e9;
		rep(i,min(h,len)){
			if(um.find(now.vechash())!=um.end()){
				len=i;
				break;
			}
			um[now.vechash()]=i;
			int A,B;
			A=now.v[0];
			B=now.v[1];
			now.v[0]=B;
			now.v[1]=A*y+B*x+z;
			now.v[1]%=MOD;
		}
		MAT iv=(M^h).getinv();
		rb(i,0,6666666){
			if(1ll*h*i>=len) break;
			rep(j,p){
				LL tmp=match[j].vechash();
				if(um.find(tmp)!=um.end()){
					int pos_=um[tmp]+1+i*h;
					if(pos_>len) continue;
					if(app[j]){
						len=pos_-app[j];
						app[j]=pos_;
						continue;
					}
					app[j]=pos_;
					pos.PB(pos_);
				}
				match[j]=match[j]*iv;
			}
		}
	}
	sort(ALL(pos));
	rb(i,1,q){
		LL l,r;
		scanf("%lld %lld",&l,&r);
		printf("%lld\n",get(r)-get(l-1));
	}
}
int main(){
//	freopen("test.in","r",stdin);
//	freopen("test.out","w",stdout);
	scanf("%d",&T);
	rb(i,1,T) solve();
	return 0;
}

posted @ 2021-01-14 22:08  WWW~~~  阅读(73)  评论(0编辑  收藏  举报