zoukankan      html  css  js  c++  java
  • sss

    <更新提示>

    <第一次更新>


    <正文>

    公交线路

    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
    ",f[n-K+1][tot]);
        return 0;
    }
    

    这样的动态规划算法的时间复杂度为(O(nC_{p-1}^{k-1})),使用矩阵乘法加速,时间复杂度为(O((C_{p-1}^{k-1})^3log_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
    ",f[tot]);
        return 0;
    }
    

    <后记>

  • 相关阅读:
    POJ3159 Candies —— 差分约束 spfa
    POJ1511 Invitation Cards —— 最短路spfa
    POJ1860 Currency Exchange —— spfa求正环
    POJ3259 Wormholes —— spfa求负环
    POJ3660 Cow Contest —— Floyd 传递闭包
    POJ3268 Silver Cow Party —— 最短路
    POJ1797 Heavy Transportation —— 最短路变形
    POJ2253 Frogger —— 最短路变形
    POJ1759 Garland —— 二分
    POJ3685 Matrix —— 二分
  • 原文地址:https://www.cnblogs.com/Parsnip/p/11520457.html
Copyright © 2011-2022 走看看