[bzoj4851][Jsoi2016]位运算【矩阵乘法】【状压dp】

【题目链接】
  https://www.lydsy.com/JudgeOnline/problem.php?id=4851
【题解】
  题目中说选的数两两都不相同,那么我们状压记是否满足了不同的限制,并且规定前一个比后一个大。再开一位记最前面的一个是否小于S。转移时,2n枚举这一位填什么。然后判断是否合法。我们的目标是求出对于每一种开始的状态,在|S|步后到每种状态的方案数。这样的复杂度是O(2N2N2N|S|N)的,不可以接受。
  观察后发现,在位数相同时,转移是固定的,所以可以预处理出所有的转移。这样复杂度变为了O(2N2NN|S|+2N2N2N|S|)可以接受。
  接下来就是套路的矩阵乘法。复杂度O((2N)3logK)
  

/* --------------
    user Vanisher
    problem bzoj-4851 
----------------*/
# include <bits/stdc++.h>
# define    ll      long long
# define    inf     0x3f3f3f3f
# define    N       7
# define    L       51
# define    P       1000000007
using namespace std;
int read(){
    int tmp=0, fh=1; char ch=getchar();
    while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
    while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
    return tmp*fh;
}
struct Metrix{
    int num[1<<N][1<<N];
    void none(){
        memset(num,0,sizeof(num));
        for (int i=0; i<(1<<N); i++)
            num[i][i]=1;
    }
}mp,nex;
Metrix operator *(Metrix a, Metrix b){
    for (int i=0; i<(1<<N); i++)
        for (int j=0; j<(1<<N); j++){
            nex.num[i][j]=0;
            for (int t=0; t<(1<<N); t++)
                nex.num[i][j]=(nex.num[i][j]+1ll*a.num[i][t]*b.num[t][j])%P;
        }
    return nex;
}
int f[L][1<<N],n,k,num[N+1],to[1<<N][1<<N][L];
char s[L],l;
int cmp(int x, int y, int las){
    if (las!=0) return 1;
    if (x>y) return -1;
    if (x==y) return 0;
    return 1;
}
Metrix mypow(Metrix x, int y){
    Metrix i=x; x.none();
    while (y>0){
        if (y%2==1) x=x*i;
        i=i*i;
        y/=2;
    }
    return x;
}
int main(){
    n=read(), k=read();
    scanf("\n%s",s+1); l=strlen(s+1);
    for (int i=1; i<=l; i++) s[i]=s[i]-'0';
    for (int i=0; i<(1<<n); i++)
        for (int j=0; j<(1<<n); j++){
            int cnt=0;
            for (int t=1; t<=n; t++) num[t]=((j&(1<<(t-1)))!=0);
            for (int t=1; t<=n; t++) cnt=cnt+num[t];
            if (cnt%2==0){
                for (int p=1; p<=l; p++){
                    num[0]=s[p]; int now=0, flag=true;
                    for (int t=1; t<=n; t++){
                        int tmp=cmp(num[t],num[t-1],i&(1<<(t-1)));
                        if (tmp==-1){flag=false; break;}
                        now=now+tmp*(1<<(t-1));
                    }
                    if (flag) to[i][j][p]=now;
                        else to[i][j][p]=-1;
                }
            }
            else for (int p=1; p<=l; p++)
                to[i][j][p]=-1;
        }
    for (int i=0; i<(1<<n); i++){
        memset(f,0,sizeof(f));
        f[0][i]=1;
        for (int j=1; j<=l; j++)
            for (int t=0; t<(1<<n); t++){
                if (f[j-1][t]==0) continue;
                for (int p=0; p<(1<<n); p++){
                    int now=to[t][p][j];
                    if (now!=-1) f[j][now]=(f[j][now]+f[j-1][t])%P;
                } 
            }
        for (int t=0; t<(1<<n); t++)
            mp.num[i][t]=f[l][t];
    }
    mp=mypow(mp,k);
    printf("%d\n",mp.num[0][(1<<n)-1]); 
    return 0;
}
posted @ 2018-04-14 20:17  Vanisher  阅读(144)  评论(0编辑  收藏  举报