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

这个状压状态时显然的,但是总状态数有 $\binom{K}{P}$.  

好在题目中有一个要求,就是每个格子必须经过一次,所以说我们压缩的长度为 $P$ 的状态中首位必为 1.  

那么状态数就减小为 $\binom{K-1}{P-1}$,来一个矩阵乘法就行了. 

code: 

#include <cstdio> 
#include <algorithm> 
#include <cstring>   
#define ll long long 
#define mod 30031  
#define N 303    
#define setIO(s) freopen(s".in","r",stdin)  
using namespace std; 
int cnt[1<<12],mark[1<<12],idx[1<<12];
struct M 
{  
    int m[130][130];         
    M(int t=0) 
    {
        memset(m,0,sizeof(m));   
        for(int i=1;i<130;++i) m[i][i]=t;   
    }
    int *operator[](int x) { return m[x]; }     
    M operator*(const M b) const 
    {      
        M c(0);   
        for(int i=1;i<130;++i) 
            for(int j=1;j<130;++j)  
                for(int k=1;k<130;++k)  
                    (c[i][j]+=(ll)m[i][k]*b.m[k][j]%mod)%=mod;   
        return c;   
    }   
    friend M operator^(M a,int k)  
    {
        M tmp(1);   
        while(k) 
        {
            if(k&1) tmp=tmp*a;   
            a=a*a,k>>=1;  
        }
        return tmp;  
    }
}v,A;  
int main() 
{ 
    // setIO("input"); 
    int n,K,P,cn=0;  
    scanf("%d%d%d",&n,&K,&P);          
    for(int i=1;i<(1<<P);++i) 
    {
        cnt[i]=cnt[i-(i&(-i))]+1;            
        if(cnt[i]==K&&(i&(1<<(P-1)))) mark[i]=1,idx[i]=++cn;                 
    }   
    for(int i=1;i<(1<<P);++i) 
    {
        if(!mark[i])  continue;    
        if(i&1)  v[idx[i]][idx[(i>>1)|(1<<(P-1))]]=1;       
        else 
        {               
            for(int j=0;j<P;++j)  
                if(i&(1<<j))       
                    v[idx[i]][idx[((i^(1<<j))>>1)|(1<<(P-1))]]=1;   
        }
    }    
    A=v^(n-K);       
    int an=idx[(1<<P)-(1<<(P-K))];    
    printf("%d\n",A[an][an]);  
    return 0; 
}

  

posted @ 2020-06-02 10:44  EM-LGH  阅读(164)  评论(0编辑  收藏  举报