CF1152F
能学到很多的题。
如何构造一个序列?
朴素:按照下标一位一位构造。
特殊:按随机顺序依次在某个元素前后插入。
再特殊:从小到大插入。
这里因为限制是 \(a_i\le a_{i-1}+m\),所以从小到大插入很有优势,因为每个数 \(x\),仅能插入到 \([x-m,x-1]\) 之后。
因为任意两个数不能相等,所以启发我们可以在值域上搞。
但显然我们还需要压值域 \([i-m,i]\) 的状态,因为 \(i\) 能插的位置仅仅在这些位置之后或者开头。因为 \(a_i\le a_{i-1}+m\),显然 \(\min\{a_{i-1}\}=a_i-m\),又因为是值域上一个个做下去,所以 \(\max\{a_{i-1}\}=i-1\),所以对于当前枚举的值域 \([1,i]\),我们需要记录 \([i-m,i]\) 的状态为 \(S\)。
记 \(T=2^{m+1}-1\),因为压 \([i-m,i]\) 总共 \(m+1\) 个数。
记 \(las=(S<<1)\&T\),值域向右平移一位嘛。
不填,判 \(S\) 最高位为 0。\(f[i][j][S]=f[i-1][j][las]+f[i-1][j][las|1]\)
填,判 \(S\) 最高位为 1。\(f[i][j][S]=(f[i-1][j-1][las]+f[i-1][j-1][las|1])*(pop\_count(las)+1)\)
#include <bits/stdc++.h>
//#define int long long
#define pb push_back
using namespace std;
const int mod=(int)(1e9+7),N=(int)(1e5+1);
int f[N][13][(1ll<<5)],n,k,m;
signed main() {
cin.tie(0); ios::sync_with_stdio(false);
cin>>n>>k>>m;
int T=(1<<(m+1))-1; //1 1
f[0][0][0]=1;
for(int i=1;i<=n;i++) {
for(int j=0;j<=k;j++) {
for(int S=0;S<=T;S++) {
bool fl=(S>>m)&1;
if(fl&&!j) continue ;
int las=(S<<1)&T;
int qwq=__builtin_popcount(las)+1;
if(!fl) f[i][j][S]=((f[i-1][j][las]+f[i-1][j][las|1])%mod+f[i][j][S])%mod;
if(fl) f[i][j][S]=((1ll*f[i-1][j-1][las]*qwq%mod+1ll*f[i-1][j-1][(las|1)]*qwq%mod)%mod+f[i][j][S])%mod;
// cout<<i<<" "<<j<<" "<<S<<" "<<las<<" "<<f[i][j][S]<<'\n';
}
}
}
int ans=0;
for(int S=0;S<=T;S++) {
ans=(ans+f[n][k][S])%mod;
}
cout<<ans;
return 0;
}
F2 矩乘优化
https://www.cnblogs.com/xugangfan/p/16495212.html
#include <bits/stdc++.h>
//#define int long long
#define pb push_back
using namespace std;
const int mod=(int)(1e9+7),N=(int)(1e5+1);
int n,k,m,tot,id[14][1<<4];
struct Mat {
int a[230][230];
Mat() {
memset(a,0,sizeof(a));
}
void mul(Mat g) {
Mat res;
for(int i=1;i<=tot;i++)
for(int j=1;j<=tot;j++)
for(int k=1;k<=tot;k++)
res.a[i][j]=(res.a[i][j]+1ll*a[i][k]*g.a[k][j]%mod)%mod;
*this=res;
}
}ans,B;
signed main() {
cin.tie(0); ios::sync_with_stdio(false);
cin>>n>>k>>m;
int T=(1<<m)-1; //1 1
int CH=(1<<(m-1));
// for(int i=0;i<n;i++) {
// for(int j=0;j<=k;j++) {
// for(int S=0;S<=T;S++) {
// int NEX=(S>>1)&T;
// int qwq=__builtin_popcount(S>>1)+1;
// f[i+1][j+1][NEX|CH]=(f[i+1][j+1][NEX|CH]+1ll*f[i][j][S]*qwq%mod)%mod;
// f[i+1][j][NEX]=(f[i+1][j][NEX]+f[i][j][S])%mod;
// } //抽象成一条边,因为矩阵乘法每次多经过 1 条边,相乘->方案数
// }
// }
tot=0;
for(int j=0;j<=k;j++)
for(int S=0;S<=T;S++)
id[j][S]=++tot;
for(int j=0;j<=k;j++) {
for(int S=0;S<=T;S++) {
int NEX=(S>>1)&T,qwq=__builtin_popcount(S)+1;
B.a[id[j][S]][id[j][NEX]]=(B.a[id[j][S]][id[j][NEX]]+1)%mod;
if(j<k) B.a[id[j][S]][id[j+1][NEX|CH]]=(B.a[id[j][S]][id[j+1][NEX|CH]]+qwq)%mod;
}
}
ans=B; --n;
while(n) {
if(n&1) ans.mul(B);
n>>=1; B.mul(B);
}
int answ=0;
for(int S=0;S<=T;S++) {
answ=(answ+ans.a[id[0][0]][id[k][S]])%mod;
}
cout<<answ;
return 0;
}