hdu 2604 DP+矩陣二分


實際上就是這樣一個問題,一個序列僅由1和0組成,問n位不帶101和111子序列有多少個,結果模m

話說這是集訓的一道題,當時完全沒思路,今天做了一下,還是沒有做出來,不過好歹還會用最裸的記憶化搜索。。。但肯定超時(話說集訓那時真的弱爆了。。超弱的1B)

看了下解題報告,發現1維DP既能搞定,再用矩陣二分。。其實這個DP怎么推出來的呢?就是分類討論出來的。。(哎,其實真的很簡單,但偏偏想亂了,這方面題果斷刷的太少)。。設F(n)為n位滿足題意的數列數。。。當第1位為0時,F(n)=F(n-1),若第二位為1,那么肯定第三位必須要為0,這個時候在看第二位,若第二位為0時,F(n)=F(n-3),若第二位為1時,那嗎只有1100...滿足條件,而第5位無論是神馬也滿足,F(n)=F(n-4)...所以F(n)=F(n-1)+F(n-3)+F(n-4)...矩陣快速冪即可。。(注意雖然是3項,但矩陣一定是4項,剛開始因為寫成3項就WA了。。。)

#include <iostream>

#define N 25
#define M 4
#define cl(a) memset(a,0,sizeof(a))
#define ss(a) scanf("%d",&a)
 
using namespace std;
 
int n,m,f[5],u[N][5][5],t[5];
const int a[5]={0,2,4,6,9};
const int b[5]={0,1,0,1,1};

void init()
{
    int i,j;
    cl(f);cl(u);
    for (i=1;i<=M;i++) f[i]=a[5-i]%m;
    for (i=1;i<=M;i++)
        for (j=1;j<=M;j++)
            if (i==1) u[0][i][j]=b[j];
            else if (i==j+1) u[0][i][j]=1;
            else u[0][i][j]=0;
}

void mi(int x)
{
    int i,j,k;
    for (i=1;i<=M;i++)
        for (j=1;j<=M;j++)
        {
            int r=0;
            for (k=1;k<=M;k++) r=(r+u[x-1][i][k]*u[x-1][k][j])%m;
            u[x][i][j]=r; 
        }    
    return;
}

void mul(int x)
{
    int i,j,k;
    cl(t);
    for (i=1;i<=M;i++)
        for (j=1;j<=M;j++) 
            t[i]=(t[i]+u[x][i][j]*f[j])%m;
    for (i=1;i<=M;i++) f[i]=t[i];
}

void bin(int n1)
{
    int i=0; 
    while (n1>0)
    {
        if (n1&1) mul(i);
        i++;
        n1>>=1;
    }
}
            
int main()
{
    int i;
    while (ss(n)!=EOF)
    {
        ss(m);
        if (n<=4)
        {
            printf("%d\n",a[n]%m);
            continue;
        }
        init();
        for (i=1;i<N;i++) mi(i);
        bin(n-4);
        int res=f[1];
        printf("%d\n",res);
    }
    return 0;
}


 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com