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;
}