每个长度为p的区间都必须出现k次1,数据又很小,我们使用状压。

dp[i][j]->dp[i+1][j'],dp[i][j]表示当前考虑到了第i个车站,包括第i个其后的p个的状态(有车停或没车停),其中j'为j中某一个车动一次到达的状态,为了防止复杂度爆炸,先dfs求一遍有用的状态。

然后矩阵转移。。

另外提一下初末状态,注意到起始站的限制,初末状态都应是p个里前k个都是1。(在代码实现中就正好对应了第一个dfs的状态)

这也是开始B.t[1][1]赋值1和最后输出B.t[1][1]的原因

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=200,mod=30031;
struct matr{
    int t[maxn][maxn],n,m;
    matr operator*(matr &a){
        matr c;
        for(int i=1;i<=n;++i)
          for(int j=1;j<=a.m;++j){
            c.t[i][j]=0;
              for(int k=1;k<=n;++k)
                c.t[i][j]=(c.t[i][j]+t[i][k]*a.t[k][j])%mod;
        }
        c.n=n;c.m=a.m;
        return c;
    }
}A,B;
int n,k,p,q[maxn];
void dfs(int pos,int num,int cnt){
    if(cnt==k){q[++q[0]]=num;return;}
    for(int i=pos-1;i>=0;--i)
    dfs(i,num+(1<<i),cnt+1);
}
int check(int x,int y){
    int tmp=x-(1<<(p-1));
    tmp<<=1;tmp^=y;
    if(tmp==(tmp&(-tmp)))return 1;
    return 0;
}
int main(){
    cin>>n>>k>>p;
    dfs(p-1,(1<<(p-1)),1);
    A.n=A.m=B.n=B.m=q[0];
    for(int i=1;i<=q[0];++i)B.t[i][i]=1;
    for(int i=1;i<=q[0];++i)
      for(int j=1;j<=q[0];++j){
        if(check(q[i],q[j]))A.t[i][j]=1;
    }
    /*for(int i=1;i<=q[0];++i){
      for(int j=1;j<=q[0];++j)
        cout<<A.t[i][j]<<' ';
        cout<<endl;
    }*/
    int cc=n-k;
    while(cc){
        if(cc&1)B=B*A;
        cc>>=1;A=A*A;
    }
    cout<<B.t[1][1]<<endl;
    //system("pause");
    return 0;
}

 

posted on 2018-01-18 21:03  湮灭之瞳  阅读(110)  评论(0编辑  收藏  举报