CF1152F Neko Rules the Catniverse
Link
简化题意:
给定正整数\(n,k,m\),求有多少个满足下列条件的长度为\(k\)的序列\(\{a\}\):
\(1.\forall1\le i<j\le k,a_i\ne a_j\)
\(2.\forall i\in[1,k],a_i\in[1,n]\)
\(3.\forall i\in(1,k],a_i\le a_{i-1}+m\)
考虑从小往大考虑每个数,假如我们的数是\(x\),那么我们现在可以选择放或不放。
如果放,那么我们可以放在序列末尾,或者是某个\([x-m,x)\)范围内的数的前面。
把\([x-m,x)\)的数是否在序列中状压即可转移。
设\(f_{i,j,k}\)表示当前的数是\(i\),序列中已经放了\(j\)个数,\([i-m,i)\)的数的状态为\(k\)时的答案。
设\(S=2^m-1\),转移如下:
不放:\(f_{i,j,k}\rightarrow f_{i+1,j,k<<1\&S}\)
放:\((\operatorname{bit}k+1)f_{i,j,k}\rightarrow f_{i+1,j+1,k<<1\&S|1}\)
直接做是\(O(nk2^m)\)的。注意到后面转移与\(i\)无关,所以把后面两维压成一维,然后矩阵快速幂优化即可,时间复杂度为\(O((k2^m)^3\log n)\)。
#include<cstdio>
#include<cstring>
const int P=1000000007;
int read(){int x;scanf("%d",&x);return x;}
void inc(int&a,int b){a+=b-P,a+=a>>31&P;}
int tot,id[13][16],cnt[16];
struct matrix{int a[220][220];matrix(){memset(a,0,sizeof a);}int*operator[](int x){return a[x];}}E,I;
matrix operator*(matrix a,matrix b)
{
matrix c;
for(int i=1;i<=tot;++i) for(int j=1;j<=tot;++j) for(int k=1;k<=tot;++k) c[i][j]=(c[i][j]+1ll*a[i][k]*b[k][j])%P;
return c;
}
int main()
{
int n=read(),k=read(),m=read(),S=(1<<m)-1,ans=0;I[1][1]=1;
for(int i=1;i<=S;++i) cnt[i]=cnt[i>>1]+(i&1);
for(int i=0;i<=k;++i) for(int s=0;s<=S;++s) id[i][s]=++tot;
for(int i=0;i<=k;++i)
for(int s=0,t;s<=S;++s)
{
inc(E[id[i][s]][id[i][t=s<<1&S]],1);
if(i^k) inc(E[id[i][s]][id[i+1][t|1]],cnt[s]+1);
}
for(;n;n>>=1,E=E*E) if(n&1) I=I*E;
for(int i=0;i<1<<m;++i) inc(ans,I[1][id[k][i]]);
printf("%d",ans);
}