[HEOI2012]赵州桥——矩阵乘法+循环节
矩阵乘法
一题两做,出题人是个智障
首先,推式子:
先考虑涂颜色方案数,忽略掉
设
1、如果第一个格子颜色和上一行第二个相同,那么这一行第二个格子有
2、如果第一个格子颜色和上一行第二个不同,那么这一行第二个格子有
所以
即
然后化简一下就是
然后把题目要求的
这里
第二步,
发现:
然后二项式定理,把式子拆开就可以了。
矩阵需要维护
复杂度
然后矩阵乘法的时候,如果
这样就有
代码(
#include<bits/stdc++.h>
#define N 205
using namespace std;
int n,m,p,k;
int c[N][N];
struct nd{int a[N][N];}b,t,now;
inline int POW(int a,int b,int ans=1){
for(;b;b>>=1,a=1ll*a*a%p)
if(b&1) ans=1ll*ans*a%p;
return ans;
}
void init()
{
k=(m*m-3*m+3)%p;
for(int i=0;i<=m;i++)
for(int j=0;j<=i;j++)
c[i][j]=j ? (c[i-1][j-1]+c[i-1][j])%p : 1;
for(int i=0;i<=m;i++)
b.a[i][0]=1;
for(int i=0;i<=m;i++)
for(int j=0;j<=i;j++)
now.a[i][j]=1ll*k*c[i][j]%p;
now.a[m+1][m+1]=now.a[m+1][m]=1;
}
inline nd mul(nd a,nd b){
memset(t.a,0,sizeof t.a);
for(int i=0;i<=m+1;i++)
for(int k=0;k<=m+1;k++) if(a.a[i][k])
for(int j=0;j<=m+1;j++) if(b.a[k][j])
t.a[i][j]=(1ll*a.a[i][k]*b.a[k][j]+t.a[i][j])%p;
return t;
}
inline nd POW(nd a,int b){
nd ans=a;b--;
for(;b;b>>=1,a=mul(a,a))
if(b&1) ans=mul(ans,a);
return ans;
}
int work()
{
b=mul(POW(now,n-1),b);
return b.a[m+1][0]+b.a[m][0];
}
signed main(){
cin>>n>>m>>p;init();
int ans=work();
cout<<1ll*ans*POW(2,m)%p*m*(m-1)%p;
}
第三步,
发现:
所以这个式子是有循环节的,循环节长度就是
因为
代码(AC):
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
#define N 205
#define int long long
using namespace std;
int n,m,p,k;
int c[N][N];
struct nd{int a[N][N];}b,t,now;
inline int POW(int a,int b,int ans=1){
for(;b;b>>=1,a=1ll*a*a%p)
if(b&1) ans=1ll*ans*a%p;
return ans;
}
void init()
{
for(int i=0;i<=m;i++)
for(int j=0;j<=i;j++)
c[i][j]=j ? (c[i-1][j-1]+c[i-1][j])%p : 1,now.a[i][j]=1ll*k*c[i][j]%p;
for(int i=0;i<=m;i++) b.a[i][0]=1;
now.a[m+1][m+1]=now.a[m+1][m]=1;
}
inline nd mul(nd a,nd b){
memset(t.a,0,sizeof t.a);
for(int i=0;i<=m+1;i++)
for(int k=0;k<=m+1;k++) if(a.a[i][k])
for(int j=0;j<=m+1;j++) if(b.a[k][j])
t.a[i][j]=(1ll*a.a[i][k]*b.a[k][j]+t.a[i][j])%p;
return t;
}
nd POW(nd a,int b){
nd ans=a;b--;
for(;b;b>>=1,a=mul(a,a))
if(b&1) ans=mul(ans,a);
return ans;
}
int work(){
init(); b=mul(POW(now,n-1),b);
return b.a[m+1][0]+b.a[m][0];
}
int cal(int b,int n){
if(n==1) return b;
if(n&1) return 1ll*(cal(b,n-1)+1)*b%p;
return 1ll*cal(b,n>>1)*(1+POW(b,n>>1))%p;
}
int work2()
{
int now=0;
for(int i=1;i<=p;i++)
now=(now+1ll*POW(i,m)%p*POW(k,i-1))%p;
now=1ll*now*(cal(POW(k,p),n/p-1)+1)%p;
for(int i=n-n%p+1;i<=n;i++)
now=(now+1ll*POW(i,m)%p*POW(k,i-1))%p;
return now;
}
signed main(){
cin>>n>>m>>p;k=(m*m-3*m+3)%p;
int ans=m<=200 ? work() : work2();
cout<<1ll*ans*POW(2,m)%p*m*(m-1)%p;
}