[bzoj2004][Hnoi2010]Bus 公交线路——动态规划+矩阵快速幂

本文介绍了一种解决公交线路规划问题的算法,通过动态规划和矩阵快速幂技术,计算满足特定条件下的线路设计方案数量。考虑到公交车站布局和行驶限制,算法巧妙地处理了始发站与终点站的特殊性,确保了每辆公交车在规定距离内运行。

题目大意:

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

设共K辆公交车,则1到K号站作为始发站,N-K+1到N号台作为终点站。
每个车站必须被一辆且仅一辆公交车经过(始发站和终点站也算被经过)。
公交车只能从编号较小的站台驶往编号较大的站台。
一辆公交车经过的相邻两个站台间距离不得超过Pkm。
在最终设计线路之前,小Z想知道有多少种满足要求的方案。由于答案可能很大,你只需求出答案对30031取模的结果。

思路:

显然前面的k个车站和后面的k个车站需要特殊处理,然后中间的地方可以直接dp。
dp状态记录每一辆车目前离这个点最近的停靠站是哪个,转移的规则是每辆车相邻两个停靠车站之间距离\(\leq p\)
考虑这样的状态总共有多少个,每一个点都会有一辆车停靠,于是一定会有一个0,然后每一辆车的停靠点距离这个点距离应该\(< p\),于是最终的状态数只有\({p-1\choose k}\),直接将所有的状态搜出来,然后处理一下状态之间转移的关系后矩阵快速幂即可。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define MREP(i,x) for(int i=beg[x],v;v=to[i],i;i=las[i])
#define debug(x) cout<<#x<<"="<<x<<endl
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
    freopen("bzoj2004.in","r",stdin);
    freopen("bzoj2004.out","w",stdout);
}

template<typename T>void read(T &_){
    T __=0,mul=1; char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')mul=-1;
        ch=getchar();
    }
    while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
    _=__*mul;
}

const int mod=30031;
const int maxn=200+10;
int n,p,k,ans;
int st[maxn][11],cnt;

struct Matrix{
    int c[maxn][maxn];
    Matrix(){REP(i,1,cnt)REP(j,1,cnt)c[i][j]=0;}
};

Matrix operator * (Matrix a,Matrix b){
    Matrix ret;
    REP(i,1,cnt)REP(j,1,cnt)REP(l,1,cnt)
        ret.c[i][j]=(ret.c[i][j]+a.c[i][l]*b.c[l][j])%mod;
    return ret;
}

namespace Find_States{
    int now[11];
    void dfs(int s,int f){
        if(s>k){
            ++cnt;
            REP(i,1,k)st[cnt][i]=now[i];
            return;
        }
        REP(i,f,p-1){
            now[s]=i;
            dfs(s+1,i+1);
        }
    }
}

void Match(Matrix &a){
    int t[11];
    REP(i,1,cnt){
        REP(l,1,k){
            bool lim=false;
            REP(r,1,k)if(r==l)t[r]=0;
            else{
                t[r]=st[i][r]+1;
                if(t[r]>=p){lim=true;break;}
            }
            if(lim)continue;

            sort(t+1,t+k+1);
            REP(j,1,cnt){
                bool flag=true;
                REP(r,1,k)if(t[r]!=st[j][r]){flag=false;break;}
                if(flag)a.c[i][j]=1;
            }
        }
    }
}

Matrix qpow(Matrix x,int y){
    Matrix ret;
    REP(i,1,cnt)ret.c[i][i]=1;
    while(y){
        if(y&1)ret=ret*x;
        x=x*x;
        y>>=1;
    }
    return ret;
}

void work(){
    Matrix a,t;
    t.c[1][1]=1;
    Match(a);
    a=t*qpow(a,n-k*2);

    REP(i,1,cnt){
        DREP(j,k,1)a.c[1][i]=a.c[1][i]*(min(p-st[i][j],k)-k+j)%mod;
        ans=(ans+a.c[1][i])%mod;
    }

    printf("%d\n",ans);
}

int main(){
    File();

    read(n),read(k),read(p);

    Find_States::dfs(2,1);

    work();

    return 0;
}

转载于:https://www.cnblogs.com/ylsoi/p/10113421.html

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值