『公交线路 状压dp 矩阵乘法加速』

Posted parsnip

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了『公交线路 状压dp 矩阵乘法加速』相关的知识,希望对你有一定的参考价值。

<更新提示>

<第一次更新>


<正文>

公交线路

Description

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

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

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

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

4.一辆公交车经过的相邻两个

站台间距离不得超过Pkm。 在最终设计线路之前,小Z想知道有多少种满足要求的方案。由于答案可能很大,你只

需求出答案对30031取模的结果。

Input Format

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

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

Output Format

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

Sample Input

10 2 4

Sample Output

81

解析

问题转换:求个\(n\)个变量\(x_1-n\)\([1,k]\)之间的值的方案数,需要满足:\(x_1-k\)取遍\(1-k\)\(x_(n-k+1)-n\)取遍\(1-k\),任意一个区间\([t,t+p-1]\)\(1-k\)的数至少出现一次。

距离的限制其实就是上述问题转换中的最后一条限制。

考虑状压\(dp\),由于\(p\)很小,我们就压一下当前阶段那个长度为\(p\)的区间的赋值情况。也就是说,\(f[i][S]\)代表前\(i-1\)个变量一个完成赋值,区间\([i,i+p-1]\)\(1-k\)这些数的赋值情况为\(S\)的方案数。\(S\)是一个长度为\(p\)的二进制数,共有\(k\)\(1\),从最高位开始数,\(S\)的第\(j\)位为\(1\),代表\(x_i+j-1\)被赋值为了一个\(1-k\)之间的数(我们不关心每一个数的具体位置,只需要保证这\(k\)个数在这个区间内每个出现至少一次即可),作为当前区间合法性的关键值,其他变量随意。对于任意的\(S\),需满足最高位为\(1\),是因为我们必须保证第\(i\)位取,这样才能保证最后所有变量都赋了值。

转移当然是由上一个阶段转移而来,方程就是:
\[f[i][S]=\sum_valid(S',S) f[i-1][S']\]

如何判定两个状态直接可以转移?首先两个状态的区间只差了一个位置。也就是\(S'\)的最高位在\(S\)中不存在,那么我们就把最高位取走,然后在最低位后面补\(0\),如果此时\(S'\)\(S\)的子集,那么我们只需在第\(i+p-1\)这个位置补上刚才取走的那个数就又是合法的方案了。所以,当操作完后的状态\(S'\)\(S\)的子集时,可以转移。

\(Code:\)

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+20 , SIZE = 130 , Mod = 30031;
int n,K,P,f[N][SIZE],S[SIZE],tot,ans;
inline void input(void)

    scanf("%d%d%d",&n,&K,&P);

inline int add(int a,int b)  return a + b >= Mod ? a + b - Mod : a + b; 
inline void Add(int &a,int b)  a = add( a , b ); 
inline void DynamicProgram(void)

    for (int i=1<<P-1;i<1<<P;i++)
    
        int cnt = 0;
        for (int j=1;j<=P;j++)
            cnt += ( i >> (j-1) & 1 );
        if ( cnt == K ) S[++tot] = i;
    
    f[1][tot] = 1;
    for (int i=2;i<=n-K+1;i++)
        for (int j=1;j<=tot;j++)
            for (int k=1;k<=tot;k++)
            
                int S1 = S[k] , S2 = S[j];
                int S3 = S1 - (1<<P-1) << 1;
                for (int l=1;l<=P;l++)
                    if ( not( S3 >> (l-1) & 1 ) && ( S3 + (1<<l-1) == S2 ) )
                        Add( f[i][j] , f[i-1][k] );
            

int main(void)

    input();
    DynamicProgram();
    printf("%d\n",f[n-K+1][tot]);
    return 0;

这样的动态规划算法的时间复杂度为\(O(nC_p-1^k-1)\),使用矩阵乘法加速,时间复杂度为\(O((C_p-1^k-1)^3\log_2n)\)

\(Code:\)

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+20 , SIZE = 130 , Mod = 30031;
int n,K,P,S[SIZE],f[SIZE],tot;
inline int add(int a,int b)  return a + b >= Mod ? a + b - Mod : a + b; 
inline int mul(int a,int b)  return 1LL * a * b % Mod; 
inline void Add(int &a,int b)  a = add( a , b ); 
struct Matrix

    int mat[SIZE][SIZE];
    Matrix ()  memset( mat , 0 , sizeof mat ); 
    friend Matrix operator * (Matrix a,Matrix b)
    
        Matrix c;
        for (int i=1;i<=tot;i++)
            for (int j=1;j<=tot;j++)
                for (int k=1;k<=tot;k++)
                    Add( c.mat[i][j] , mul( a.mat[i][k] , b.mat[k][j] ) );
        return c;
    
;
Matrix Trans;
inline void Maxtrixmul(int *a,Matrix b)

    int c[SIZE] = ;
    for (int j=1;j<=tot;j++)
        for (int k=1;k<=tot;k++)
            Add( c[j] , mul( a[k] , b.mat[k][j] ) );
    memcpy( a , c , sizeof c );

inline void DynamicProgram(void)

    for (int i=1<<P-1;i<1<<P;i++)
    
        int cnt = 0;
        for (int j=1;j<=P;j++)
            cnt += ( i >> (j-1) & 1 );
        if ( cnt == K ) S[++tot] = i;
    
    for (int i=1;i<=tot;i++)
        for (int j=1;j<=tot;j++)
        
            int S1 = S[i] , S2 = S[j];
            int S3 = S1 - (1<<P-1) << 1;
            for (int k=1;k<=P;k++)
                if ( not( S3 >> (k-1) & 1 ) && ( S3 + (1<<k-1) == S2 ) )
                    Trans.mat[i][j] = 1;
        
    int T = n - K; f[tot] = 1;
    while ( T )
    
        if ( 1 & T ) Maxtrixmul( f , Trans );
        Trans = Trans * Trans , T >>= 1;
    

int main(void)

    scanf("%d%d%d",&n,&K,&P);
    DynamicProgram();
    printf("%d\n",f[tot]);
    return 0;

<后记>

以上是关于『公交线路 状压dp 矩阵乘法加速』的主要内容,如果未能解决你的问题,请参考以下文章

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

矩阵乘法加速图上问题专题总结

矩阵乘法&&dp加速矩阵的思路(E. Wet Shark and Blocks)

BZOJ 4000: [TJOI2015]棋盘( 状压dp + 矩阵快速幂 )

矩阵乘法加速器的设计框架

使用加速框架的矩阵乘法和逆问题

(c)2006-2024 SYSTEM All Rights Reserved IT常识