Codeforces 107 D. Crime Management
問題
文字列に対しての条件がc個与えられる。
- アルファベットa[i]がちょうどw[i]の整数倍個だけ含まれている
- ただし、同じアルファベットに対してw[i]が複数個定義されている場合、w[i]のどれかの倍数になっていればよい。
長さnの文字列で条件を満たすものの個数をmod 12345で求めよ。
条件に一度も現れていないアルファベットは1回も使ってはならないものとする。
制約条件
n≦10^18
c≦1000
w[i]の全ての積≦123
方針
長さnの時にそれぞれのアルファベットのw[i]の余りがr[i]であるときの場合の数を求めたい。
これは、c個の余りを1つの整数にコーディングすれば、w[i]の全ての積が123以下であることから、123以下の整数として表せる。
新たに末尾に一文字を加えるときに、r[i]がどのように遷移するかを行列として表せば、
長さnの場合の数が行列のn乗として表せることになる。
行列のn乗を繰り返し二乗法により求めた後、余りが全て条件を満たしている成分を足し合わせれば、答えの場合の数が求まる。
ソースコード
const int mod=12345; mat operator*(const mat &a,const mat &b){ mat res(a.size(),vi(b[0].size())); rep(i,res.size())rep(j,res[0].size())rep(k,a[0].size())(res[i][j]+=a[i][k]*b[k][j])%=mod; return res; } mat pow(mat m,ll n){ mat res(m.size(),vi(m.size())); rep(i,res.size())res[i][i]=1; for(;n;n>>=1){ if(n&1)res=res*m; m=m*m; } return res; } void run(){ ll n; int c; cin>>n>>c; vi v[26]; int szs[26]; rep(i,c){ char a; int m; cin>>a>>m; v[a-'A'].pb(m); } int sz=1; rep(i,26){ int lcm=1; rep(j,v[i].size())lcm=lcm*v[i][j]/__gcd(lcm,v[i][j]); sz*=lcm; szs[i]=lcm; } mat M(sz,vi(sz)); rep(i,sz){ vi nums; int m=i; rep(j,26){ nums.pb(m%szs[j]); m/=szs[j]; } rep(j,26){ vi nxt=nums; nxt[j]=(nxt[j]+1)%szs[j]; int m=0; for(int k=25;k>=0;k--)m=m*szs[k]+nxt[k]; if(!v[j].empty())M[m][i]++; } } M=pow(M,n); int ans=0; rep(i,sz){ vi nums; int m=i; bool ok=1; rep(j,26){ nums.pb(m%szs[j]); m/=szs[j]; bool ok2=v[j].empty(); rep(k,v[j].size())if(nums[j]%v[j][k]==0)ok2=1; if(!ok2)ok=0; } if(ok)ans+=M[i][0]; } cout<<ans%mod<<endl; }