2021-2022年度第三届全国大学生算法设计与编程挑战赛(秋季赛)- 分组(矩阵快速幂套NTT优化dp)
Posted Frozen_Guardian
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021-2022年度第三届全国大学生算法设计与编程挑战赛(秋季赛)- 分组(矩阵快速幂套NTT优化dp)相关的知识,希望对你有一定的参考价值。
题目链接:点击查看
题目大意:给出 n n n 个连续的小球,每次可以选择单独的一个或者相邻的两个小球分成一组,允许有剩余的小球,问恰好分成 k ∈ { 1 , 2 , 3 , ⋯ , m } k\\in\\{1,2,3,\\cdots,m\\} k∈{1,2,3,⋯,m} 组的方案数分别是多少。
题目分析:设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 为前
i
i
i 个小球分成
j
j
j 组的方案数,不难推出:
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
j
]
+
d
p
[
i
−
1
]
[
j
−
1
]
+
d
p
[
i
−
2
]
[
j
−
1
]
dp[i][j]=dp[i-1][j]+dp[i-1][j-1]+dp[i-2][j-1]
dp[i][j]=dp[i−1][j]+dp[i−1][j−1]+dp[i−2][j−1]
由于不会特征方程求解通项公式,只会用矩阵快速幂套多项式优化上述过程,时间复杂度 O ( m log n log m ) O(m\\log n\\log m) O(mlognlogm),比赛时无论如何卡常都 T L E TLE TLE,但是补题的时候交了一发很神奇的卡常卡过去了,离谱。
然后这个模板在矩阵自乘的时候做了一些小优化,将原本 8 8 8 次的 D F T DFT DFT 下降成了 4 4 4 次,可以说是优化了很多了。
具体推导可以参考:2021HDU多校8 - 7057 Buying Snacks
一份常数更小的代码:
#include<bits/stdc++.h>
using namespace std;
#define SZ(x) ((int)(x).size())
#define rep(i,a) for(int i=0;i<(a);++i)
#define repi(i,a) for(int i=1;i<=(a);++i)
template<typename T>
inline void write(T x)
{
if(x<0){x=~(x-1);putchar('-');}
if(x>9)write(x/10);
putchar(x%10+'0');
}
typedef long long ll;
const int Inf=0x3f3f3f3f;
const int jt=998244353,rg=3,mlg=20;
void inline add(int &a,int b) {
a+=b-jt;
a+=(a>>31)&jt;
}
void inline sub(int &a,int b) {
a-=b;
a+=(a>>31)&jt;
}
void inline mul(int &a,int b) {
a=(ll)a*b%jt;
}
int inline Add(int a,int b) {
return a+b>=jt?a+b-jt:a+b;
}
int inline Sub(int a,int b) {
return a-b<0?a-b+jt:a-b;
}
int inline Mul(int a,int b) {
return (ll)a*b%jt;
}
typedef vector<int>Poly;
int inline ksmii(int a,int b) {
int res=1;
while(b) {
if(b&1) {
mul(res,a);
}
mul(a,a);
b>>=1;
}
return res;
}
namespace Fourier {
int ho[mlg+1][1<<mlg],omg[mlg+1][1<<mlg],in[mlg+1];
void init() {
rep(lg,mlg+1) {
int N=1<<lg;
if(lg) {
rep(i,N) {
ho[lg][i]=(ho[lg][i>>1]>>1)|((i&1)<<(lg-1));
}
}
int omega=ksmii(rg,(jt-1)>>(lg+1)),ome=1;
rep(i,N) omg[lg][i]=ome,mul(ome,omega);
in[lg]=ksmii(N,jt-2);
}
}
void inline fft(int a[],int lg,int inv) {
int N=1<<lg;
rep(i,N) {
if(ho[lg][i]<i) {
swap(a[i],a[ho[lg][i]]);
}
}
rep(l,lg) {
int i=1<<l;
for(int j=0;j<N;j+=i<<1) {
int *x=a+j,*y=x+i,*o=omg[l],tmp;
rep(k,i) {
tmp=Mul(*y,*o);
*y=Sub(*x,tmp);
add(*x,tmp);
++x,++y,++o;
}
}
}
if(!~inv) {
reverse(a+1,a+N);
rep(i,N) {
mul(a[i],in[lg]);
}
}
}
}
int N,M;
int tmp[2][2][1<<mlg],tmq[2][2][1<<mlg],tmr[2][2][1<<mlg];
struct Matrix {
Poly a[2][2];
inline Poly *operator[](int x) {
return a[x];
}
inline const Poly *operator[](int x) const {
return a[x];
}
inline Matrix operator*(const Matrix &b) const {
int ts1=0,ts2=0;
rep(i,2) rep(j,2) ts1=max(ts1,SZ(a[i][j]));
rep(i,2) rep(j,2) ts2=max(ts2,SZ(b[i][j]));
int ts=ts1+ts2,lg=0;
while((1<<lg)<ts) ++lg;
int N=1<<lg;
rep(i,2) rep(j,2) {
rep(k,N) tmp[i][j][k]=k<SZ(a[i][j])?a[i][j][k]:0;
Fourier::fft(tmp[i][j],lg,1);
}
rep(i,2) rep(j,2) {
rep(k,N) tmq[i][j][k]=k<SZ(b[i][j])?b[i][j][k]:0;
Fourier::fft(tmq[i][j],lg,1);
}
rep(i,2) rep(j,2) rep(k,N) tmr[i][j][k]=0;
rep(p,N) rep(i,2) rep(j,2) rep(k,2) add(tmr[i][k][p],Mul(tmp[i][j][p],tmq[j][k][p]));
rep(i,2) rep(j,2) Fourier::fft(tmr[i][j],lg,-1);
Matrix res;
if(ts>M+5) ts=M+5;
rep(i,2) rep(j,2) {
res[i][j].resize(ts);
rep(k,ts) res[i][j][k]=tmr[i][j][k];
}
return res;
}
};
void MUL(Matrix &a){
int ts=0;
rep(i,2) rep(j,2) ts=max(ts,SZ(a[i][j]));
int lg=0;
ts*=2;
while((1<<lg)<ts) ++lg;
int N=1<<lg;
rep(i,2) rep(j,2) {
rep(k,N) tmp[i][j][k]=k<SZ(a[i][j])?a[i][j][k]:0;
Fourier::fft(tmp[i][j],lg,1);
}
rep(i,2) rep(j,2) rep(k,N) tmr[i][j][k]=0;
rep(p,N) rep(i,2) rep(j,2) rep(k,2) add(tmr[i][k][p],Mul(tmp[i][j][p],tmp[j][k][p]));
rep(i,2) rep(j,2) Fourier::fft(tmr[i][j],lg,-1);
if(ts>M+5) ts=M+5;
rep(i,2) rep(j,2) {
a[i][j].resize(ts);
rep(k,ts) a[i][j][k]=tmr[i][j][k];
}
}
inline Matrix ksmii(Matrix a,int b) {
Matrix res;
rep(i,2) rep(j,2) res[i][j]=i==j?Poly{1}:Poly{};
while(b2021-2022年度第三届全国大学生算法设计与编程挑战赛(冬季赛)-正式赛 部分题解
2021-2022年度第三届全国大学生算法设计与编程挑战赛(冬季赛正式赛) 部分题题解
2021-2022年度第三届全国大学生算法设计与编程挑战赛(冬季赛正式赛) 部分题题解
2021-2022年度第三届全国大学生算法设计与编程挑战赛(秋季赛)- 占座位(最小割)