[NOIP2021] 数列 题解
[NOIP2021] 数列 题解
知识点
状压组合计数 DP。
分析
DP 套路
-
把无序的变成有序的:
我们按照下标遍历 \(\{v_i\}\),一个个插入到最终的答案序列,并用组合数转化,那么这样无序的就变成了有序的。
-
观察数据范围上限:
发现按照顺序遍历后,进位始终比 \(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;
}

浙公网安备 33010602011771号