zoukankan      html  css  js  c++  java
  • [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
    ", 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
    ", a.tmp[End][End]);
    70     }
    71 }

  • 相关阅读:
    20200929-git地址
    20200917-1 每周例行报告
    20200917-2 词频统计
    20200917-3 白名单
    20200910-1 每周例行报告
    20200910-2 博客作业
    20190919-6 四则运算试题生成,结对
    20190919-3 效能分析
    20190919-2 功能测试
    20190912-1 每周例行报告
  • 原文地址:https://www.cnblogs.com/sainsist/p/11169345.html
Copyright © 2011-2022 走看看