1. 程式人生 > >[bzoj2004] [洛谷P3204] [Hnoi2010] Bus 公交線路

[bzoj2004] [洛谷P3204] [Hnoi2010] Bus 公交線路

快速 efi 包含 位運算 距離 所在 pan 矩陣快速冪 依次

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


想法

emm這個題還是有難度的。
我想到的第一版dp為
\(f[i][st‘]+=f[i-1][st]\)
f[i][st]中的st為八進制p位數,表示哪些公交車經過 (i-p+1) 到 i 這連續p個站臺
由於公交車相鄰兩者站臺間距離不超過p,所以st中應出現所有公交車。
轉移時註意st‘與st必須滿足st的後p-1位與st‘的前p-1位相同。

這樣是正確的。但顯然時間空間都承受不了。

考慮原先的dp有哪些東西是不必要的。
註意到我們轉移的時候,從st到st‘,並沒有用到經過某一站臺的公交車編號是多少,只關心st與st‘是否合法(即是否出現所有公交車)以及是否可以成功轉移。
那麽把st變為一個二進制p位數,其中某x位上的1代表有一個公交車在這p個站臺中最後經過的站臺為x
只要st中有k個1,且最後一位為1便是合法的。
從st到st‘,只要st的後p-1位與st‘的前p-1位至多有一位不同便可以成功轉移。

但這樣狀態為\(2^p\),仍有點多。

不過可以發現滿足條件的st必須有k個1且最後一位為1,這樣狀態數就減為了 \(C_{p-1}^{k-1}\),最多也就二百多。
之後就可以矩陣快速冪了。


代碼

細節還是有的,二進制位運算的地方要註意一些。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
 
#define P 30031
 
using namespace std;
 
const int SZ=260;
 
int tot;
struct matrix{
    int a[SZ][SZ];
    matrix() { memset(a,0,sizeof(a)); }
    void init() { for(int i=0;i<tot;i++) a[i][i]=1; }
    matrix operator * (const matrix &b) const{
        matrix c;
        for(int i=0;i<tot;i++)
            for(int j=0;j<tot;j++)
                for(int k=0;k<tot;k++)
                    (c.a[i][j]+=a[i][k]*b.a[k][j])%=P;
        return c;
    }
    matrix operator *= (const matrix &b) { return *this=*this*b; }
};
matrix Pow_mod(matrix x,int y){
    matrix ret; ret.init();
    while(y){
        if(y&1) ret*=x;
        x*=x;
        y>>=1;    
    }
    return ret;
}
 
int n,p,k;
int num[1030];
 
int cal(int x){
    int ret=0;
    while(x){
        ret+=(x&1);
        x>>=1;    
    }
    return ret;
}
void getnum(){
    for(int i=0;i<(1<<p);i++)
        if(cal(i)==k && (i&1)==1) num[tot++]=i;
}
bool check(int x,int y){
    if((y&1)==0) return false;
    int z=(x%(1<<(p-1)))^(y>>1);
    return z==(z&(-z));
}
 
int main()
{
    scanf("%d%d%d",&n,&k,&p);
    getnum();
     
    matrix a,b;
    for(int i=0;i<tot;i++)
        for(int j=0;j<tot;j++)
            if(check(num[i],num[j]))
                a.a[i][j]++;
    b.a[0][0]=1; 
    a=Pow_mod(a,n-k); /**/
    b=b*a;
     
    printf("%d\n",b.a[0][0]);
     
    return 0;   
}

[bzoj2004] [洛谷P3204] [Hnoi2010] Bus 公交線路