1. 程式人生 > >Constructing Chimney(HDU-4332)

Constructing Chimney(HDU-4332)

ict 個人 表示 stat 能夠 定義 描述 fin enter

題目描述:

Now we are planning to construct a big chimney. The chimney’s section is a 3×3 square and the center of it will have nothing like the picture below. Now we only have a lot of bricks whose size if 1×1×2, and we want to build a chimney whose height is N, so can help us to calculate how many ways we can build the chimney? The answer may be very large, so you can just tell the answer‘s remainder divided by 1000000007.

技術分享圖片

用1×1×2的磚頭擺出如圖所示的煙囪,可以橫著擺也可以豎著擺,求擺出n層高的煙囪會有多少種不同的方案。

思路:

這題其實最難想的就是dp轉移,變態的細節非常的多。

首先要考慮的就是狀態定義,若是一層一層的進行轉移我們都考慮他們所在當前層的狀態,我們用一個8位二進制數來表示當前層的狀態,當然,1表示磚,0表示沒有磚。

若是暴力轉移我們便有:
$dp[i][stat]=dp[i-1][stat2]*met[stat][stat2]$

註意$met[stat][stat2]$表示狀態由當前層的stat2到下一層的stat的方案數

若是直接這樣轉移,復雜度絕對爆炸,所以就考慮用矩陣乘法快速冪來進行操作。

不過在開始定義矩陣時,先想一想:就算使用矩陣優化,復雜度也是$O(256^3*log(n))$,還是很可能會超時(不過卡常卡的好話或許也可以吧。。個人沒試過)。那麽咋們看一下這$256$個狀態(畢竟那個log也沒什麽好優化的),應該比較好想到所有$1$的個數為奇數個的狀態都是無效的,因為這樣就無法把當前層填滿而又不會填到下一層了。這樣復雜度就夠了,不過好像可以再把所有旋轉相同的狀態也去掉,使得最後只有七十多種狀態,不過旋轉判定有點復雜,我就懶得想了。。。

最後就是預處理最初矩陣,其實就是把所有能夠相互轉移的狀態找出,註意是當前層的狀態轉移到下一層的狀態,所以$0$$1$是哪一層的$0$$1$

分清楚,別的也沒什麽了,判是否可行的方法有很多,就不在過多贅述了。

代碼

#include<bits/stdc++.h>
#define FOR(i,l,r) for(int i=l,END=r;i<=END;i++)
#define DOR(i,r,l) for(int i=r,END=l;i>=END;i--)
#define loop(i,n) for(int i=0,END=n;i<END;i++)
#define mms(a,x) memset(a,x,sizeof a)
#define sf scanf
#define pf printf
#define lowbit(x) ((x)&(-x))
using namespace std;
const int P=1e9+7,N=(1<<8),M=N>>1;
int n;
bool flag[N];
struct Matrix{//矩陣 
    int num[N][N];
    void clear(){mms(num,0);}
    void init(){clear();loop(i,M)num[i][i]=1;}
    void Print(){loop(i,M)loop(j,M)pf("%d%c",num[i][j],j==M-1?'\n':' ');}
    Matrix operator*(const Matrix &A)const{
        Matrix ans;
        ans.clear();
        loop(i,M)loop(j,M)
            loop(k,M)ans.num[i][j]=(ans.num[i][j]+1ll*num[i][k]*A.num[k][j])%P;
        return ans;
    }
}Pow[35];
bool is1(int x,int pos){return (x>>pos)&1;}
bool check(int x,int y){//判定是否可行 
    int mark=2;
    loop(i,8){
        if(is1(x,i)&&is1(y,i)){//若在上一層已有,那麽只能橫著放磚塊,所以它的下一位也要是1 
            if(i<7&&is1(x,i+1)&&is1(y,i+1))i++;
            else {mark--;break;};   
        }
        else if(!is1(x,i)&&!is1(y,i)){mark--;break;};
    }
    x|=(x&1)<<8;
    y|=(y&1)<<8;
    //把第一位移到最後面
    DOR(i,8,1){//到這再來一次,這次能夠使第一個和最後一個也考慮到 
        if(is1(x,i)&&is1(y,i)){//同上
            if(i>1&&is1(x,i-1)&&is1(y,i-1))i--;
            else {mark--;break;};
        }
        else if(!is1(x,i)&&!is1(y,i)){mark--;break;};       
    }
    return mark>0;
}
void init(){
    loop(i,N){
        int t=i,cnt=0;
        for(;t;t^=lowbit(t))cnt^=1;
        if(!cnt)flag[i]=1;
    }
    int bx=0,by=0;
    loop(i,N){//預處理矩陣 
        if(flag[i]){
            by=0;
            loop(j,N)if(flag[j])
                Pow[0].num[bx][by++]=check(i,j);
            bx++;
        }
    }
    Pow[0].num[M-1][M-1]++;
    FOR(i,1,32)Pow[i]=Pow[i-1]*Pow[i-1];//預處理快速冪矩陣 
}
int qpow(int n){
    Matrix res;
    res.init();
    loop(i,32)if(is1(n,i))res=res*Pow[i];
    return res.num[M-1][M-1];
}
int main(){
    init();
    int T,kase=0;
    sf("%d",&T);
    while(T--){
        sf("%d",&n);
        pf("Case %d: %d\n",++kase,qpow(n));
    }
    return 0;
}

Constructing Chimney(HDU-4332)