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;
    }
    

    <后记>

  • 相关阅读:
    GitHub 6大热门实时人脸识别开源项目!哪个最适合初级开发者?
    人脸识别相关开源项目汇总
    Nginx设置成服务并开机自动启动
    Linux安装nginx
    nginx根据域名转发
    14个开源免费的人工智能项目,人脸识别依旧很受欢迎
    linux 双网卡内外网访问
    000webhost – 1500M支持PHP可绑米免费虚拟主机
    CPUID
    Apache中.htaccess文件功能
  • 原文地址:https://www.cnblogs.com/Parsnip/p/11520457.html
Copyright © 2011-2022 走看看