[HNOI2010]公交线路
XXIII.[HNOI2010]公交线路
状压+矩乘的好题。
因为每\(p\)个位置中,每辆车就至少有\(1\)个位置,
所以我们可以状压一下。
设\(f[i][j]\)表示:
区间\([i,i+p-1]\)内的车站现在的规划情况是\(j\)的方案数。
显然,必有\(j\)的第\(p\)位是\(1\),且\(j\)共有\(k\)位是\(1\)(\(j\)的第\(p\)位对应着\(i\))。
则\(f[i][j]=\sum f[i-1][k]\),其中\(k\)能转移到\(j\)。
那什么样的\(k\)能转移到\(j\)呢?
我们将\(k\)左移一位(即增加了 \(i+p-1\) 一位),然后删去第\(p\)位的数(即删去第 \(i-1\) 位),得到了一个\(k'\)。
如果\(k'\)和\(j\)只相差恰好\(1\)位,那么\(k\)就可以转移到\(j\)(第\(i-1\)位的车刚好跑到了着相差的一位)。
然后发现,对于每个\(f[i][j]\),它的祖先的\(k\)都是完全一致的;因此可以矩乘优化,建立转移矩阵\(T[k][j]\),如果状态\(k\)可以转移到\(j\),则\(T[k][j]=1\),否则为\(0\)。
则复杂度为\(S^3\log n\),其中\(S\)是合法状态数量(即符合\(j\)的第\(p\)位是\(1\),且\(j\)共有\(k\)位是\(1\)的\(j\)的数量)。我们有\(S=C_{p-1}^{k-1}\),当\(p=10,k=5\ \operatorname{or}\ 6\)时取得最大值,有\(S=C_9^4\ \operatorname{or}\ C_9^5=126\)。
代码:
#include<bits/stdc++.h>
using namespace std;
const int mod=30031;
int n,m,p,len,sta[150];
struct mat{
int g[150][150];
mat(){memset(g,0,sizeof(g));}
friend mat operator *(const mat &x,const mat &y){
mat z;
for(int i=0;i<len;i++)for(int j=0;j<len;j++)for(int k=0;k<len;k++)(z.g[i][j]+=x.g[i][k]*y.g[k][j])%=mod;
return z;
}
}X,I;
bool che(int x,int y){
x<<=1,x-=(1<<p);
return __builtin_popcount(x^y)<=1;
}
void ksm(int y){
for(;y;X=X*X,y>>=1)if(y&1)I=I*X;
}
int main(){
scanf("%d%d%d",&n,&m,&p);
for(int i=(1<<(p-1));i<(1<<p);i++)if(__builtin_popcount(i)==m)sta[len++]=i;
for(int i=0;i<len;i++)I.g[i][i]=1;
for(int i=0;i<len;i++)for(int j=0;j<len;j++)X.g[i][j]=che(sta[i],sta[j]);
ksm(n-m);
printf("%d\n",I.g[len-1][len-1]);
return 0;
}