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