[Hnoi2010]Bus 公交线路

Description
小Z所在的城市有N个公交车站,排列在一条长(N-1)km的直线上,从左到右依次编号为1到N,相邻公交车站间的距离均为1km。
作为公交车线路的规划者,小Z调查了市民的需求,决定按下述规则设计线路:

  • 设共K辆公交车,则1到K号站作为始发站,N-K+1到N号台作为终点站。
  • 每个车站必须被一辆且仅一辆公交车经过(始发站和终点站也算被经过)。
  • 公交车只能从编号较小的站台驶往编号较大的站台。
  • 一辆公交车经过的相邻两个站台间距离不得超过Pkm。

在最终设计线路之前,小Z想知道有多少种满足要求的方案。
由于答案可能很大,你只需求出答案对30031取模的结果。

Input
仅一行包含三个正整数N K P,分别表示公交车站数,公交车数,相邻站台的距离限制。
N<=10^9,1<P<=10,K<N,1<K<=P

Output
仅包含一个整数,表示满足要求的方案数对30031取模的结果。

Sample Input 1
10 3 3

Sample Output 1
1

Sample Input 2
5 2 3

Sample Output 2
3

Sample Input 3
10 2 4

Sample Output 3
81

看到\(p\leqslant 10\),这题肯定要状压;看到\(n\leqslant 10^9\),这题肯定要矩阵乘法

\(f[i][sta]\)表示最快的公交车到了第\(i\)个车站,当前停靠公交车的站台的状态为\(sta\)(\(sta\)上第\(k\)位为1说明第\(k\)个车站上停了车),很容易推出转移方程式为\(f[i][sta]=\sum\limits_{j=k}^{i-1}f[j][sta']\)\(sta'\)\(sta\)的转移是合法的。

然后注意到题目有限制:一个公交车经过的两个相邻的站台之间的距离不超过\(p\),这样可以把\(sta\)的枚举范围从\(\[1,2^n-1\]\)缩小到\(\[1,2^p-1\]\),那么空间上就没啥问题了。

然后我们发现对于每个\(f[i][sta]\)来说,拿来转移到它的\(sta'\)都是一样的,那么我们可以用矩乘优化来加速这个DP

/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline int read(){
    int x=0,f=1;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar())  if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar())    x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}
inline void print(int x){
    if (x>=10)  print(x/10);
    putchar(x%10+'0');
}
const int N=1.5e2,MOD=30031;
int n,k,p,tot;
struct Matrix{
    int v[N+10][N+10];
    Matrix(){memset(v,0,sizeof(v));}
    void init(){for (int i=1;i<=tot;i++)    v[i][i]=1;}
}trans,Ans;
Matrix operator *(const Matrix &x,const Matrix &y){
    Matrix z;
    for (int i=1;i<=tot;i++)
        for (int j=1;j<=tot;j++)
            for (int k=1;k<=tot;k++)
                z.v[i][k]=(z.v[i][k]+x.v[i][j]*y.v[j][k])%MOD;
    return z;
}
Matrix mlt(Matrix a,int b){
    Matrix res; res.init();
    for (;b;b>>=1,a=a*a)    if (b&1)    res=res*a;
    return res;
}
int calc(int x){
    int res=0;
    while (x) res++,x-=lowbit(x);
    return res;
}
bool check(int Now,int To){
    Now<<=1;
    int tmp=0;
    for (int i=0;i<p;i++)   if ((Now&(1<<i))^(To&(1<<i)))   tmp++;
    return tmp<2;
}
int stack[(1<<10)+10];
int main(){
    n=read(),k=read(),p=read();
    int Endl=0;
    for (int sta=(1<<(p-1));sta<1<<p;sta++){
        if (calc(sta)==k){
            stack[++tot]=sta;
            if (sta==(1<<p)-(1<<(p-k))) Endl=tot;
        }
    }
    for (int i=1;i<=tot;i++)
        for (int j=1;j<=tot;j++)
            if (check(stack[i],stack[j]))
                trans.v[i][j]=1;
    Ans.v[1][Endl]=1;
    Ans=Ans*mlt(trans,n-k);
    printf("%d\n",Ans.v[1][Endl]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Wolfycz/p/9690165.html