Description

考虑一个长度为n的序列,你需要用颜色1~m依次对序列染色
每次染色只能选择一段区间来染,要求最后整个序列没有未染色的元素。

问最终的颜色序列的种类数,答案对998244353取模。

Solution

我们考虑这样一个DP

[JZOJ6082]【GDOI2019模拟2019.3.25】染色问题【多项式】【DP】_DP表示当前染了前i种颜色,已经覆盖了[JZOJ6082]【GDOI2019模拟2019.3.25】染色问题【多项式】【DP】_#define_02个元素。

[JZOJ6082]【GDOI2019模拟2019.3.25】染色问题【多项式】【DP】_#define_03

[JZOJ6082]【GDOI2019模拟2019.3.25】染色问题【多项式】【DP】_#define_04

这是一个很妙的DP
考虑第i种颜色是否会在最后的序列中出现。
若不会,就是[JZOJ6082]【GDOI2019模拟2019.3.25】染色问题【多项式】【DP】_多项式_05
如果会,那么考虑出现这个颜色之前覆盖了多少个元素,当前覆盖了j个元素,由于新覆盖的元素必然在已经覆盖的元素中是连续的(去掉未覆盖的部分),那么相当于一个长度为j的序列选出一段长度为[JZOJ6082]【GDOI2019模拟2019.3.25】染色问题【多项式】【DP】_Code_06的区间,那么方案数就是[JZOJ6082]【GDOI2019模拟2019.3.25】染色问题【多项式】【DP】_多项式_07

答案就是[JZOJ6082]【GDOI2019模拟2019.3.25】染色问题【多项式】【DP】_多项式_08(因为最后一种颜色必然出现)

前缀和优化可以做到[JZOJ6082]【GDOI2019模拟2019.3.25】染色问题【多项式】【DP】_#define_09
考虑优化,观察式子的实际意义,我们相当于在[JZOJ6082]【GDOI2019模拟2019.3.25】染色问题【多项式】【DP】_Code_10中选择了若干个不重复的数乘在一起,这些数可以有m次机会取,每一次可以取可以不取,要求先取的比后取的小。
并且要求1必须取。

m的贡献只与取多少个数有关
构造多项式
[JZOJ6082]【GDOI2019模拟2019.3.25】染色问题【多项式】【DP】_#define_11
前面那个x表示1必须取

那么[JZOJ6082]【GDOI2019模拟2019.3.25】染色问题【多项式】【DP】_DP_12,其中[JZOJ6082]【GDOI2019模拟2019.3.25】染色问题【多项式】【DP】_多项式_13[JZOJ6082]【GDOI2019模拟2019.3.25】染色问题【多项式】【DP】_Code_14[JZOJ6082]【GDOI2019模拟2019.3.25】染色问题【多项式】【DP】_DP_15项系数。

先把x去掉,i从1开始
这个多项式显然可以倍增求

倍增的过程可以考虑一下如何从[JZOJ6082]【GDOI2019模拟2019.3.25】染色问题【多项式】【DP】_Code_14[JZOJ6082]【GDOI2019模拟2019.3.25】染色问题【多项式】【DP】_Code_17,计算一下要乘上的多项式的系数即可。

那么时间复杂度就是[JZOJ6082]【GDOI2019模拟2019.3.25】染色问题【多项式】【DP】_多项式_18

Code

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 1000005
#define M 1048576
#define LL long long
#define mo 998244353
using namespace std;
LL js[M+1],ns[M+1],ny[M+1];
int n,m;

//prepare
LL ksm(LL k,LL n)
{
LL s=1;
for(;n;n>>=1,k=k*k%mo) if(n&1) s=s*k%mo;
return s;
}

LL C(int n,int m)
{
if(n<m) return 0;
return js[n]*ns[m]%mo*ns[n-m]%mo;
}

//polynomial
namespace polynomial
{
LL wi[M+1],wg[M+1],u1[M+1],u2[M+1],a[M+1],b[M+1];
int bit[M+1],cf[21],l2[M+1];
void pre()
{
wg[0]=1,wg[1]=ksm(3,(mo-1)/M),js[0]=js[1]=1;
fo(i,2,M) wg[i]=wg[i-1]*wg[1]%mo,js[i]=js[i-1]*(LL)i%mo;
ns[M]=ksm(js[M],mo-2);
ny[M]=ns[M]*js[M-1]%mo;
ns[0]=1;
fod(i,M-1,1) ns[i]=ns[i+1]*(LL)(i+1)%mo,ny[i]=ns[i]*js[i-1]%mo;
cf[0]=1;
fo(i,1,20) l2[cf[i]=cf[i-1]<<1]=i;
fod(i,M,2) if(!l2[i]) l2[i]=l2[i+1];
}
void prp(int num)
{
fo(i,0,num)
{
bit[i]=(bit[i>>1]>>1)|((i&1)<<(l2[num]-1));
wi[i]=wg[i*(M/num)];
}
}
void NTT(LL *a,bool pd,int num)
{
fo(i,0,num-1) if(i<bit[i]) swap(a[i],a[bit[i]]);
for(int m=2,h=1,l=num>>1;m<=num;h=m,m<<=1,l>>=1)
{
int c=(!pd)?l:-l;
for(int j=0;j<num;j+=m)
{
LL *x=a+j,*y=a+j+h,*w=(!pd)?wi:wi+num,v;
fo(i,0,h-1)
{
v=*y * *w%mo;
*y=(*x-v+mo)%mo;
*x=(*x+v)%mo;
x++,y++,w+=c;
}
}
}
if(pd) fo(i,0,num-1) a[i]=a[i]*ny[num]%mo;
}
void mul(int num,LL *a,LL *b)
{
NTT(a,0,num),NTT(b,0,num);
fo(i,0,num-1) a[i]=a[i]*b[i]%mo;
NTT(a,1,num);
}
}

using namespace polynomial;

void make(int n)
{
int c=cf[l2[n+1]-1]>>1;
a[0]=1,a[1]=1;
int le=1;
while(c>0)
{
int num=cf[l2[le+1]+1];
prp(num);
fo(i,0,num-1) u1[i]=u2[i]=0;
LL v=1;
fo(i,0,le) u1[i]=a[i]*js[le-i]%mo,u2[i]=ns[i]*v%mo,v=v*(LL)le%mo;
mul(num,u1,u2);
fo(i,0,le) u1[i]=u1[i]*ns[le-i]%mo,u2[i]=a[i];
fo(i,le+1,num) u1[i]=u2[i]=0;
mul(num,u1,u2);
le<<=1;
fo(i,0,le) a[i]=u1[i];
if(n&c) {le++;fod(i,le,1) a[i]=(a[i]+(LL)le*a[i-1])%mo;}
c>>=1;
}
}

LL calc(int m,int n)
{
LL s=0;
fo(i,1,m)
s=(s+C(m,i)*a[i])%mo;
return s;
}
int main()
{
cin>>n>>m;
pre();
make(n);
fod(i,n-1,0) a[i]=(a[i]-a[i+1]+mo)%mo;
printf("%lld\n",(calc(m,n)-calc(m-1,n)+mo)%mo);
}