[BZOJ2004][Hnoi2010]Bus 公交线路

试题描述

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

1.设共K辆公交车,则1到K号站作为始发站,N-K+1到N号台作为终点站。

2.每个车站必须被一辆且仅一辆公交车经过(始发站和终点站也算被经过)。

3.公交车只能从编号较小的站台驶往编号较大的站台。

4.一辆公交车经过的相邻两个站台间距离不得超过Pkm。 在最终设计线路之前,小Z想知道有多少种满足要求的方案。由于答案可能很大,你只需求出答案对30031取模的结果。

输入

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

N<=10^9,1<P<=10,K<N,1<K<=P

输出

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

输入示例

10 2 4

输出示例

81

数据规模及约定

P<=10 , K <=8

题解

状压 dp + 矩阵乘法。

话说最近还想自己出一道这两种算法结合在一起的题呢。。。

这题目标状态只需要让 K 辆车分别在最后 K 个位置即可,顺序没有规定。所以这题设计状态时也不用考虑顺序。我们令 f(i, S) 表示前 i-1 个都已经经过了,第 i 到第 i + P - 1 个位置的状态为 S 的方案数,这里的“方案”可以理解成 K 个公交车所在位置的集合,也可以理解成覆盖过的位置的集合,这两个理解是等价的(原因在于我们转移时只考虑最靠左的公交车移到哪,与此同时 i 要加 1,即我们关注的位置区间向右错一格,所以一个公交车移开之后变成“没有公交车”状态的位置不会出现在状态中)。

由于 n 很大,而每次转移都是形如 f(i, S) -> f(i+1, tS),所以可以构造转移矩阵做矩阵快速幂。

注意最靠前那一位必须是 1,这样可以省掉一部分状态。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;

int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
	return x * f;
}

#define maxS 1024
#define maxs 260
#define MOD 30031

struct Matrix {
	int n, m, A[maxs][maxs];

	Matrix() { memset(A, 0, sizeof(A)); }
	Matrix(int _, int __): n(_), m(__) { memset(A, 0, sizeof(A)); }

	Matrix operator * (const Matrix& t) const {
		Matrix ans(n, t.m);
		for(int i = 1; i <= ans.n; i++)
			for(int j = 1; j <= ans.m; j++)
				for(int k = 1; k <= m; k++)
					ans.A[i][j] = (ans.A[i][j] + A[i][k] * t.A[k][j]) % MOD;
		return ans;
	}
	Matrix operator *= (const Matrix& t) {
		*this = *this * t;
		return *this;
	}
} base, tr;

Matrix Pow(Matrix a, int b) {
	Matrix ans = a, t = a; b--;
	while(b) {
		if(b & 1) ans *= t;
		t *= t; b >>= 1;
	}
	return ans;
}

int id[maxS], cnts;

int main() {
	int n = read(), K = read(), P = read();

	int all = (1 << P) - 1;
	for(int S = 0; S <= all; S++) {
		int cnt = 0;
		for(int j = 0; j < P; j++) cnt += S >> j & 1;
		if(cnt == K && (S & 1)) id[S] = ++cnts;
	}
	tr = Matrix(cnts, cnts);
	for(int S = 0; S <= all; S++) if(id[S]) {
		int tS = S >> 1;
		for(int j = 0; j < P; j++) if(!(tS >> j & 1) && id[tS|(1<<j)]) tr.A[id[tS|(1<<j)]][id[S]]++;
	}
	base = Matrix(cnts, 1);
	base.A[1][1] = 1;

	base = Pow(tr, n - K) * base;
	printf("%d\n", base.A[1][1]);

	return 0;
}
时间: 2024-10-12 10:31:51

[BZOJ2004][Hnoi2010]Bus 公交线路的相关文章

【BZOJ2004】[Hnoi2010]Bus 公交线路 状压+矩阵乘法

[BZOJ2004][Hnoi2010]Bus 公交线路 Description 小Z所在的城市有N个公交车站,排列在一条长(N-1)km的直线上,从左到右依次编号为1到N,相邻公交车站间的距离均为1km. 作为公交车线路的规划者,小Z调查了市民的需求,决定按下述规则设计线路: 1.设共K辆公交车,则1到K号站作为始发站,N-K+1到N号台作为终点站. 2.每个车站必须被一辆且仅一辆公交车经过(始发站和终点站也算被经过). 3.公交车只能从编号较小的站台驶往编号较大的站台. 4.一辆公交车经过的

BZOJ 2004: [Hnoi2010]Bus 公交线路 [DP 状压 矩阵乘法]

传送门 题意: $n$个公交站点,$k$辆车,$1...k$是起始站,$n-k+1..n$是终点站 每个站只能被一辆车停靠一次 每辆车相邻两个停靠位置不能超过$p$ 求方案数 $n \le 10^9,\ p \le 8,\ k \le 10$ 思考过程中遇到的主要问题是“所有车是同时前进的”,既不能单独考虑一辆车又没法考虑前面的车队后面的影响 正确的做法是同时考虑所有车 每$p$个位置一定每辆车各停一次 $f[i][s]$表示当前在站点$i$,且$i$有车,$s$为车停靠状态 强制规定最靠左(即

[BZOJ 2004] [Hnoi2010] Bus 公交线路 【状压DP + 矩阵乘法】

题目链接: BZOJ - 2004 题目分析 看到题目完全不会..于是立即看神犇们的题解. 由于 p<=10 ,所以想到是使用状压.将每个连续的 p 个位置压缩成一个 p 位 2 进制数,其中共有 k 位是1,表示这 k 个位置是某辆 Bus 当前停下的位置.需要注意的是,每个状态的第一位必须是 1 ,这样保证了不会有重复的状态. 每个状态可以转移到右边的某些状态(由当前状态的第一个 1 移动).初始状态和终止状态都是前面 k 位是 1 .用矩阵转移 n - k 次. 代码 #include <

[HNOI2010]BUS 公交线路

题面 Bzoj Sol 状压很显然 重点在于转移:题目就相当与每\(p\)长度的车站必须有且仅有\(k\)个被经过 那么转移时状压的二进制一定要有\(k\)个一 且两个相邻转移的状态之间必须满足:设为\(i->j\),则\((i >> 1) \&j\)要有\(k-1\)个\(1\) 然后就可以加上矩阵快速幂优化,注意把满足要求的状态记下来,只有一百多个 我常数丑是我的错 # include <bits/stdc++.h> # define RG register #

[HNOI 2010]Bus 公交线路

Description 题库链接 有 \(N\) 个车站, \(K\) 条公交线路.第 \(1\) 到 \(K\) 站是这 \(K\) 线路的起点站.第 \(N-K+1\) 到 \(N\) 是终点站.车只会从编号小的车站驶向编号大的车站. 要求每个车站恰好只属于一个线路,而且同一个线路相临两站距离不得大于 \(P\) .求有多少种安排方法.输出答案对 \(30031\) 取余数. \(1\leq N\leq 10^9,1<K\leq P\leq 10,K<N\) Solution 不妨抛开前

【BZOJ2004】公交线路(动态规划,状态压缩,矩阵快速幂)

[BZOJ2004]公交线路(动态规划,状态压缩,矩阵快速幂) 题面 BZOJ 题解 看到\(k,p\)这么小 不难想到状态压缩 看到\(n\)这么大,不难想到矩阵快速幂 那么,我们来考虑朴素的\(dp\) 设\(f[i][j]\)表示当前位置为\(i\),前面的\(P\)个位置的状态为\(j\) 其中,状态的含义是某个公交线路最后的停靠站 如果是最后的停靠站就是\(1\),否则是\(0\) 那么,任意状态中只存在\(k\)个\(1\) 并且表示\(i\)的二进制位一定是\(1\) 所以状态相当

爱帮公交查询之公交线路查询

今天,想在微信做一个公交查询的功能.到网上找了点资料,觉得爱帮挺好的于是注册了个帐号 api 页面 http://www.aibang.com/api/usage 公交线路查询 说明 公交线路查询,该接口根据关键字查询匹配的线路. 形式 http://openapi.aibang.com/bus/lines 例如:http://openapi.aibang.com/bus/lines?app_key=f41c8afccc586de03a99c86097e98ccb&city=%E5%8C%97%

公交线路免费api接口代码

描述:本接口主要是根据城市名称 +  线路名称 模糊查找城市公交线路信息. 开源api接口:http://openapi.aibang.com/bus/lines?app_key=f41c8afccc586de03a99c86097e98ccb&city="+cityName+"&q="+line 其中cityName = URLEncoder.encode(cityName,"utf-8") line = URLEncoder.encod

苏州公交线路api 查询公交详细信息

苏州公交线路api-苏州公交状态实时跟踪,查询公交详细信息. 接口名称:苏州公交线路api 接口平台:api接口 接口地址:http://apis.juhe.cn/szbusline/bus 支持格式:JSON/XML 请求方式:GET 请求示例:http://apis.juhe.cn/szbusline/bus?key=您申请的APPKEY&bus=11 苏州公交线路api调用代码JSON返回示例: { "resultcode":"200", "