[NOIP2021] 数列 题解

[NOIP2021] 数列 题解


知识点

状压组合计数 DP。

分析

DP 套路

  1. 把无序的变成有序的:

    我们按照下标遍历 \(\{v_i\}\),一个个插入到最终的答案序列,并用组合数转化,那么这样无序的就变成了有序的。

  2. 观察数据范围上限:

    发现按照顺序遍历后,进位始终比 \(32\) 小,那么就可以动态的求解 DP。

DP 思路

那么现在用完上面两个套路后,DP 思路就显而易见了。

\(f_{i,j,q,S}\) 表示遍历到 \({v_i}\),答案序列中已经有 \(j\) 个值,二进制位中有 \(q\)\(1\),剩余进位为 \(S\)

\[i \in [0,m],j \in [0,n],q \in [0,k],S \in [0,\frac{n}2],T \in [0,n-j] \\ f_{i+1,j+T,q+(S+T \bmod 2),\frac{S+T}2} \gets f_{i,j,q,S} \times v_i^T \times {n-j \choose T} \\ \]

最后统计答案:(\(cnt(S)\) 表示 \(S\) 的二进制中有几位是 \(1\)

\[\sum f_{m+1,n,q,S} [cnt(S)+q \le k] \\ \]

代码

时间复杂度:\(O(n^3mk)\),空间复杂度:\(O(n^2mk)\)

#define Plus_Cat "sequence"
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define CNT(a) (__builtin_popcountll(a))
#define RCL(a,b,c,d) memset(a,b,sizeof(c)*(d))
#define FOR(i,a,b) for(int i(a);i<=(int)(b);++i)
#define DOR(i,a,b) for(int i(a);i>=(int)(b);--i)
#define tomax(a,...) ((a)=max({(a),__VA_ARGS__}))
#define tomin(a,...) ((a)=min({(a),__VA_ARGS__}))
#define EDGE(g,i,x,y) for(int i=(g).h[(x)],y=(g)[(i)].v;~i;y=(g)[i=(g)[i].nxt].v)
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);return Main();}signed Main
using namespace std;
constexpr int N(30+10),M(1e2+10),K(20);
namespace Modular {
#define Mod 998244353
	int C[N][N];
	template<class T1,class T2>constexpr auto add(T1 a,T2 b) {
		return a+b>=Mod?a+b-Mod:(a+b<0?a+b+Mod:a+b);
	}
	template<class T1,class T2>constexpr auto mul(T1 a,T2 b) {
		return (1ll*a*b%Mod+Mod)%Mod;
	}
	template<class T,class...Types>constexpr auto add(T a,Types...args) {
		return add(a,add(args...));
	}
	template<class T,class...Types>constexpr auto mul(T a,Types...args) {
		return mul(a,mul(args...));
	}
	template<class T1,class T2>T1 &toadd(T1 &a,T2 b) {
		return a=add(a,b);
	}
	template<class T1,class T2>T1 &tomul(T1 &a,T2 b) {
		return a=mul(a,b);
	}
	template<class T0,class T,class...Types>T0 &toadd(T0 &a,T b,Types...args) {
		return toadd(a,b),toadd(a,args...);
	}
	template<class T0,class T,class...Types>T0 &tomul(T0 &a,T b,Types...args) {
		return tomul(a,b),tomul(a,args...);
	}
	void Init(int n=N-5) {
		FOR(i,0,n) {
			C[i][0]=1;
			FOR(j,1,i)C[i][j]=add(C[i-1][j-1],C[i-1][j]);
		}
	}
} using namespace Modular;
int n,m,k,ans;
int v[N];
int f[M][N][N][K];
signed main() {
#ifdef Plus_Cat
	freopen(Plus_Cat ".in","r",stdin),freopen(Plus_Cat ".out","w",stdout);
#endif
	cin>>n>>m>>k,Init(),f[0][0][0][0]=1;
	FOR(i,0,m) {
		cin>>v[1],v[0]=1;
		FOR(j,2,n)v[j]=mul(v[j-1],v[1]);
		FOR(j,0,n)FOR(q,0,k)FOR(S,0,n>>1)FOR(T,0,n-j)
			toadd(f[i+1][j+T][q+((S+T)&1)][(S+T)>>1],mul(f[i][j][q][S],v[T],C[n-j][T]));
	}
	FOR(S,0,n>>1)FOR(q,0,k-CNT(S))toadd(ans,f[m+1][n][q][S]);
	cout<<ans<<endl;
	return 0;
}

posted @ 2024-11-13 13:52  Add_Catalyst  阅读(23)  评论(0)    收藏  举报