CF1152F Neko Rules the Catniverse(状压 DP)
CF1152F Neko Rules the Catniverse
给定参数 \(n,k,m\),你需要求有多少个大小为 \(k\) 的序列 \(a\) 满足如下三个条件:
- 任意两个元素其权值不同。
- 对于任意 \(i\) 满足 \(1\le i\le k\) 有 \(1\le a_i\le n\)。
- 对于任意 \(i\) 满足 \(2\le i\le k\) 有 \(a_i\le a_{i-1}+m\)。
答案对 \(10^9+7\) 取模。
\(1\le n\le 10^9\),\(1\le k\le \min(n,12)\),\(1\le m\le 4\)。
一眼根本没有思路,即使知道是状压也不知道如何设状态。。
\(\bigstar\texttt{Hint}\):按照位置信息的 DP 走不通,发现题目所给的是关于值域上的限制,考虑根据值域 DP。
状态怎么假设?由于有 \(n\) 总范围的限制,需要一个最大值 \(i\)。总共需要放置 \(k\) 个数,那么需要记录已经放置的数的个数。转移的时候,放入新的数 \(i\),可以放在 \(\ge i-m\) 的数的后面或者最开头的位置,所以要记录 \(\ge i-m\) 的数有哪些已经出现过了。
这样就合理推出了 DP 状态:\(f_{i,j,s}\) 表示当前放置最大值为 \(i\),已经在 \([1,i]\) 中选择了 \(j\) 个放在数列中,其中 \(\ge i-m\) 的数的选择情况为 \(s\)。
转移分为 \(i+1\) 选择或不选择两种情况:
-
选择 \(i+1\):
\[f_{i+1,j+1,(2\times s)\&All|1}\leftarrow f_{i,j,s}\times (\text{popcount}(s)+1) \] -
不选择 \(i+1\):
\[f_{i+1,j,(2\times s)\&All}\leftarrow f_{i,j,s} \]
且上面的转移方程可以写成矩阵的形式,边长为 \(k\times 2^m\),则总复杂度为 \(\mathcal{O(k^38^m\log n)}\)。
#define Maxsta 208
#define mod 1000000007
int n,K,m,lim,All,pr;
struct Matrix
{
int f[Maxsta][Maxsta];
Matrix(){ memset(f,0,sizeof(f)); }
inline Matrix friend operator * (Matrix x,Matrix y)
{
Matrix ret;
for(int i=0;i<lim;i++) for(int j=0;j<lim;j++) for(int k=0;k<lim;k++)
ret.f[i][j]=(ret.f[i][j]+1ll*x.f[i][k]*y.f[k][j]%mod)%mod;
return ret;
}
inline Matrix friend operator + (Matrix x,Matrix y)
{
Matrix ret;
for(int j=0;j<lim;j++) for(int k=0;k<lim;k++)
ret.f[0][j]=(ret.f[0][j]+1ll*x.f[0][k]*y.f[k][j]%mod)%mod;
return ret;
}
};
Matrix trans,ans;
int main()
{
n=rd(),K=rd(),m=rd(),All=1<<m,lim=(K+1)*All;
ans.f[0][0]=1;
for(int j=0;j<=K;j++) for(int s=0;s<All;s++)
{
int cur=j*All+s,nex;
if(j<K)
{
nex=(j+1)*All+(((s<<1)&(All-1))|1);
trans.f[cur][nex]=__builtin_popcount(s)+1;
}
nex=j*All+((s<<1)&(All-1));
trans.f[cur][nex]=1;
}
for(;n;n>>=1,trans=trans*trans) if(n&1) ans=ans+trans;
for(int s=0;s<All;s++) pr=(pr+ans.f[0][K*All+s])%mod;
printf("%d\n",pr);
return 0;
}