[Bzoj2004][Hnoi2010]Bus 公交线路(状压dp&&矩阵加速)

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2004

看了很多大佬的博客才理解了这道题,菜到安详QAQ

在不考虑优化的情况下,先推$dp$式子,设$dp[i][j]$为最慢的公交车走到了第$i$站,$[i,i+p-1]$站的状态为$j$时的方案数。$i$到$i+p-1$的范围内有且仅有$k$辆车,则状态$j$应该为$p$长度的二进制串,其中有且仅有$k$个$1$(表示$k$辆车)并且第$1$位一定为$1$(第$1$位对应了当前的位置)。则初始态为$dp[0][111(k个1)…000(p-k个0)]$,结束态为$dp[n-k][111(k个1)…000(p-k个0)]$。判断状态$w$是否可以转移到状态$e$,则判断$w$的第$2$位到第$p+1$位($p+1$位补零)是否与$e$的第$1$位到$p$位只有一位不同,是则可以转移$dp[i][e]+=dp[i-1][w]$;

 

这时候考虑优化,$n<=1e9$就注定要矩阵快速幂加速,则先处理出所有状态之间的关系并构建矩阵$d[i][j]$,$d[i][j]$为$1$表示第一次第$i$个状态可以转移到第$j$个状态。我们要求的是第$n-k$次后初始态到结束态的方案数,根据矩阵乘法的定义$d[i][j]=\sum_{k=1}^{n}d[i][k]*d[k][j]$,则我们只要将矩阵连乘$(n-k)$次,d[结束态][初始态]就是我们所求的。而初始态和结束态实际上是一样的。

 

当n较小时可以状压dp

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int mod = 30031;
 5 int check(int x) {//判断x状态中1的个数
 6     int sum = 0;
 7     while (x) {
 8         sum++;
 9         x -= (x&(-x));
10     }
11     return sum;
12 }
13 int dp[12][1 << 12];
14 int main() {
15     int n, k, p;
16     while (scanf("%d%d%d", &n, &k, &p) != EOF) {
17         memset(dp, 0, sizeof(dp));
18         int End;
19         for (int i = (1 << (p - 1)); i < (1 << p); i++) {
20             if (i == (1 << p) - 1 - ((1 << (p - k)) - 1))
21                 End = i;
22         }
23         dp[0][End] = 1;
24         for (int i = 1; i <= n - k; i++) {
25             for (int j = (1 << (p - 1)); j < (1 << p); j++) {
26                 for (int w = (1 << (p - 1)); w < (1 << p); w++) {
27                     if (check(j) == check(w) && check(j) == k) {
28                         int q = (w - (1 << (p - 1))) << 1;
29                         int t = (q^j);
30                         if (t == (t&(-t))) {
31                             dp[i][j] = (dp[i][j] + dp[i - 1][w]) % mod;
32                         }
33                     }
34                 }
35             }
36         }
37         printf("%d\n", dp[n - k][End]);
38     }
39 }

 

本题正解:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int mod = 30031;
 5 int check(int x) {//判断x中1的个数
 6     int sum = 0;
 7     while (x) {
 8         sum++;
 9         x -= (x&(-x));
10     }
11     return sum;
12 }
13 struct martix {
14     int tmp[150][150];
15     int num;
16     martix operator *(const martix &b)const {
17         martix ans;
18         ans.num = num;
19         for (int i = 1; i <= num; i++) {
20             for (int j = 1; j <= num; j++) {
21                 ans.tmp[i][j] = 0;
22                 for (int k = 1; k <= num; k++) {
23                     ans.tmp[i][j] += tmp[i][k] * b.tmp[k][j];
24                     ans.tmp[i][j] %= mod;
25                 }
26             }
27         }
28         return ans;
29     }
30 };
31 martix qpow(martix a, int x) {
32     martix ans;
33     ans.num = a.num;
34     memset(ans.tmp, 0, sizeof(ans.tmp));
35     for (int i = 1; i <= ans.num; i++)
36         ans.tmp[i][i] = 1;
37     while (x) {
38         if (x & 1)
39             ans = ans * a;
40         a = a * a;
41         x /= 2;
42     }
43     return ans;
44 }
45 int statu[1 << 10];
46 int main() {
47     int n, k, p;
48     while (cin >> n >> k >> p) {
49         martix a;
50         int len = 0, End;
51         for (int i = (1 << (p - 1)); i < (1 << p); i++) {
52             if (check(i) == k) {
53                 statu[++len] = i;
54                 if (i == (1 << p) - 1 - ((1 << (p - k)) - 1))
55                     End = len;
56             }
57         }
58         a.num = len;
59         memset(a.tmp, 0, sizeof(a.tmp));
60         for (int i = 1; i <= len; i++) {
61             for (int j = 1; j <= len; j++) {
62                 int q = (statu[j] - (1 << (p - 1))) << 1;
63                 int t = (q^statu[i]);
64                 if (t == (t&(-t)))
65                     a.tmp[i][j] = 1;
66             }
67         }
68         a = qpow(a, n - k);
69         printf("%d\n", a.tmp[End][End]);
70     }
71 }

 

posted @ 2019-07-11 13:13  祈梦生  阅读(169)  评论(0编辑  收藏  举报