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

posted @ 2021-03-30 15:20  Troverld  阅读(44)  评论(0编辑  收藏  举报