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;
}


posted @ 2022-07-19 17:16  FxorG  阅读(47)  评论(0编辑  收藏  举报