BZOJ 2004|HNOI 2010 Day 1|公交线路|状态压缩动态规划|矩阵乘法

本文介绍了一种基于状态压缩的公交线路规划算法,该算法适用于公交车站数量巨大而相邻车站间最大距离限定较小的情况。通过使用状态压缩技术,文章提供了一个有效的解决方案来计算满足特定条件的公交线路数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

公交线路

小 Z 所在的城市有 N 个公交车站,排列在一条长为 N1 公里的直线上,从左到右依次编号
为 1 到 N,相邻公交车站间的距离均为 1 公里。
作为公交车线路的规划者,小 Z 调查了市民的需求,决定按以下规则设计线路:
1. 设共有 K 辆公交车,则 1 到 K 号车站作为始发站,NK+1 N 号车站作为终点站。
2. 每个车站必须被一辆且仅一辆公交车经停(始发站和终点站也算被经停)。
3. 公交车只能从编号较小的车站驶向编号较大的车站。
4. 一辆公交车经停的相邻两个车站间的距离不得超过 P 公里。
注意“经停”是指经过并停车,因经过不一定会停车,故经停与经过是两个不同的概念。
在最终确定线路之前,小 Z 想知道有多少种满足要求的方案。由于答案可能很大,你只需求出
答案对 30031 取模的结果。

输入

输入文件只有一行,其中包含用空格隔开的三个正整数N, K,
P,分别表示公交车站数,公交车数,一辆公交车经停的相邻两个车站间的最大距离。输入的数据保证40%的数据满足N≤1000。100%的数据满足 1<N<109,1<P10,K<N,1<KP

输出

输出文件仅包含一个整数,表示满足要求的方案数对 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

样例解释

样例一满足要求的方案只有1种,即:(1,4,7,10),(2,5,8),(3,6,9)。样例二满足要求的方案有3种,即:(1,3,5),(2,4);(1,3,4),(2,5)和(1,4),(2,3,5)。

题解

不看数据范围不会做系列。。

发现 P 很小但N很大,于是状压。。
f[i][state]表示从i开始连续P个位置的状态为state的方案数。那么状态转移方程很显然。然后初始状态是f[1][{1~k号车站存在,k+1~p号车站不存在}],至于目标状态,为了简化情况,定为f[n-k+1][{1~k号车站存在,k+1~p号车站不存在}],当然目标状态写到f[n-p+1]也可以,代码稍长点就是了。。

#include <cstdio>
#include <cstring>
#define rep(i,j,k) for(int i=j;i<k;++i)
const int N = 155, P = 30031;
int s = 0, state[N];
struct Matrix {
    int c[N][N];
    Matrix(int x = 0) { memset(c, 0, sizeof c); rep(i,0,s) c[i][i] = x; }
    friend Matrix operator* (const Matrix &a, const Matrix &b) {
        Matrix c;
        rep(k,0,s) rep(i,0,s) rep(j,0,s)
            (c.c[i][j] += a.c[i][k] * b.c[k][j]) %= P;
        return c;
    }
    friend Matrix operator^ (Matrix a, int b) {
        Matrix c(1);
        for (; b; b /= 2, a = a * a)
            if (b & 1) c = c * a;
        return c;
    }
};

int count_bit(int x) {
    int s = 0;
    for (; x; x -= x & -x) ++s;
    return s;
}

bool check(int x, int y) {
    x >>= 1;
    int t = x ^ y;
    return t == (t & -t);
}

int main() {
    int n, k, p, x;
    scanf("%d%d%d", &n, &k, &p);
    for(int i=1;i<(1<<p);i+=2) if (count_bit(i) == k) {
        state[s] = i; if (i == (1 << k) - 1) x = s;
        ++s;
    }
    Matrix a, b;
    rep(i,0,s) rep(j,0,s)
        if (check(state[i], state[j]))
            a.c[i][j] = 1;
    b.c[0][x] = 1;
    printf("%d", (b * (a ^ (n - k))).c[0][x]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值