• BZOJ2004:[HNOI2010]Bus 公交线路(状压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

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

    Output

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

    Sample Input

    样例一:10 3 3
    样例二:5 2 3
    样例三:10 2 4

    Sample Output

    1
    3
    81

    HINT

    【样例说明】
    样例一的可行方案如下: (1,4,7,10),(2,5,8),(3,6,9)
    样例二的可行方案如下: (1,3,5),(2,4) (1,3,4),(2,5) (1,4),(2,3,5)
    P<=10 , K <=8

    Solution 

    显然这个范围是要状压+矩乘……然后我就不会了
    因为一个公交车经过的两个相邻的站台之间的距离不超过$p$,所以设$f[i][S]$表示最靠左的车在$i$位置,$i$后面$p$个位置的状态是$S$,其中$S$的某一位是0代表没车,1代表有车。
    这相当于我们把这$k$辆车都放到一个长度为$p$的区间内来做。因为车没有编号所以我们并不需要区分。
    然后状态数最大只有$C(9,4)$,所以可以预处理出所有状态可以到达的状态然后矩阵转移……$f[i][S]=sum f[i-1][S']$,其中$S'$状态可以转移到$S$。
    初始状态为长度为$p$的区间左边一段都是1,终止状态也是。

    Code

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 #define MOD (30031)
     5 using namespace std;
     6 
     7 int n,k,p,S[209],cnt,Refun;
     8 
     9 struct Matrix
    10 {
    11     int m[209][209];
    12     Matrix(){memset(m,0,sizeof(m));}
    13     Matrix operator * (const Matrix b) const
    14     {
    15         Matrix ans;
    16         for (int i=1; i<=130; ++i)
    17             for (int j=1; j<=130; ++j)
    18                 for (int k=1; k<=130; ++k)
    19                     (ans.m[i][j]+=m[i][k]*b.m[k][j])%=MOD;
    20         return ans;
    21     }
    22 }A,G;
    23 
    24 Matrix Qpow(Matrix a,int p)
    25 {
    26     Matrix ans;
    27     for (int i=1; i<=130; ++i) ans.m[i][i]=1;
    28     while (p)
    29     {
    30         if (p&1) ans=ans*a;
    31         a=a*a; p>>=1;
    32     }
    33     return ans;
    34 }
    35 
    36 int Get(int x)//二进制下1的数量 
    37 {
    38     int num=0;
    39     while (x) num+=(x&1),x>>=1;
    40     return num;
    41 }
    42 
    43 bool check(int x,int y)//判断x状态是否能到达y状态 
    44 {
    45     int now=S[x]<<1, tmp=0;
    46     for (int i=0; i<p; ++i)
    47         if ((now&(1<<i))!=(S[y]&(1<<i))) tmp++;
    48     return tmp<=1;
    49 }
    50 
    51 int main()
    52 {
    53     scanf("%d%d%d",&n,&k,&p);
    54     for (int i=1<<(p-1); i<=(1<<p)-1; ++i)//强制第一位有车 
    55         if (Get(i)==k)
    56         {
    57             S[++cnt]=i;
    58             if (S[cnt]==(1<<p)-(1<<p-k)) Refun=cnt;//记录车都在起点/终点的状态 
    59         }
    60     A.m[1][Refun]=1;
    61     for (int i=1; i<=cnt; ++i)
    62         for (int j=1; j<=cnt; ++j)
    63             if (check(i,j)) G.m[i][j]=1;
    64     G=Qpow(G,n-k);
    65     A=A*G;
    66     printf("%d
    ",A.m[1][Refun]);
    67 }
  • 相关阅读:
    简单批处理语法结构
    简单批处理常用命令
    简单批处理符号简介
    简单批处理内部命令
    jQuery操作DOM
    jQuery中的事件与动画
    jQuery选择器
    初始面向对象
    初识jQuery
    操作DOM
  • 原文地址:https://www.cnblogs.com/refun/p/9709312.html
Copyright © 2020-2023  润新知