冷静分析这题有没有什么奇怪的性质
小绿觉得被别的“连续”区间包含住的“连续”区间不够优秀
这里不妨考虑“连续”区间的包含关系.对于两个有交的“连续”区间,如果没有包含关系就是不合法的,因为“连续”区间中所有数在值域上是连续的一段,那把这两个“连续”区间的值域段拿出来,这两段一定有交(因为在原序列上就有交,在值域上同样有).所以如果把靠后的“连续”区间的左端点延伸到靠前的“连续”区间的左端点,新的区间值域等于之前两个值域段的并,它也是连续的,所以原靠后区间就不是极长的“连续”区间
那么对于一个排列,去掉最后一个元素后,剩下部分可以划分成若干极长“连续”区间,然后对于每个“连续”区间递归下去也可以得到类似结果,所以对于一个极长“连续”区间,我们从它的右端点向最后一个元素后,剩下的极长“连续”区间右端点连边,然后递归,就可以得到一个树形结构.现在要利用这个树形结构算答案,dfs整棵树,对于当前节点,因为要满足不存在 长度(>1)的不是整个区间的 “连续”区间,所以把不能存在一些相邻区间满足值域连续;又因为它的每个儿子节点代表的极长“连续”区间都是值域上连续的一段,所以可以把每个区间缩成一个数,这时加上最后一个位置,原问题可以就对应到一个长度为(i)的区间,满足不存在 长度(>1)的不是整个区间的 “连续”区间,记长度为(i)的这样排列数为(f_i),这个点就可以给答案乘上(f_{|son(x)|+1}(son(x))为(x)儿子集合())
现在考虑求(f_i).如果我们枚举所有长度为(i)的排列,那么这个排列去掉最后一位后,前面会划分成若干极长“连续”区间,我们要的是前面极长“连续”区间长度均为1的,等价于前面被划分成恰好(i-1)段.所以可以用总方案减掉前面划分段数更小的方案得到(f_i),设(g_{i,j})为长度为(i),且划分成(j)段,每段为一个排列的方案数(每段的排列代表一个值域连续的段).求不合法方案的时候同样可以把每一段缩成一个数,因为要满足一些相邻段不可以为“连续”区间,所以有(f_i=i!-sum_{j=1}^{i-2} g_{i-1,j}f_{j+1})
考虑更优秀的做法,直接从(f)入手.为了方便,我们在原排列(p)的逆排列(p^{-1})上考虑,那么原来的“连续”区间可以唯一对应逆排列的“连续”区间,原问题可以改为求满足不存在不包括最大值的极长“连续”区间的排列个数.对于长度为(i)的合法排列,我们考虑删掉里面的1
,然后把前后拼起来得到长度为(i-1)的序列列
-
如果新排列还是合法的,那么一定满足
1
和2
不相邻,这样的排列数为((i-2)f_{i-1}) -
如果新排列不是合法的,那么只会有一个不包括最大值的极长“连续”区间,我们枚举其长度(l(l>1)),方案数的话,对于这个长度(i-1)的排列同样可以把这个长度(l)区间缩成一个数,对应一个长度(i-l)合法排列;然后对于这个段,先考虑其值域([x,x+l-1]),需要满足(x>2&&x+l-1<i),前者因为如果这个段中有
2
,加上1
后还是连续的不合法段,后者是因为它不包括最大值,所以(x)取值有(i-l-2)种,然后考虑这个段的数相对顺序,由于加入1
后这个段没有包含1
的“连续”区间,所以可以把1
认为是段内最大值,对应一个长度(l+1)合法排列.这部分贡献为((i-2)f_{i-1}+sum_{l=2}^{i-2}(i-l-2)f_{i-l}f_{l+1})
然后分治FFT优化(f_i=(i-2)f_{i-1}+(i-2)f_{i-1}+sum_{l=2}^{i-3}(i-l-2)f_{i-l}f_{l+1})即可
#include<bits/stdc++.h>
#define LL long long
#define db double
using namespace std;
const int N=50000+10,M=(1<<17)+10,mod=998244353;
int rd()
{
int x=0,w=1;char ch=0;
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+(ch^48);ch=getchar();}
return x*w;
}
int to[N],nt[N],hd[N],tot=1;
void adde(int x,int y){++tot,to[tot]=y,nt[tot]=hd[x],hd[x]=tot;}
int n,T,sz[N],a[N],g[N],ans,fac[N],st[N],tp;
void dfs(int x)
{
sz[x]=1;
for(int i=hd[x];i;i=nt[i])
{
int y=to[i];
dfs(y),++sz[x];
}
ans=1ll*ans*g[sz[x]]%mod;
}
int fpow(int a,int b){int an=1;while(b){if(b&1) an=1ll*an*a%mod;a=1ll*a*a%mod,b>>=1;}return an;}
int ginv(int a){return fpow(a,mod-2);}
int W[16],iW[16],rdr[M];
void ntt(int *a,int n,bool op)
{
int l=0,y;
while(1<<l<n) ++l;
for(int i=0;i<n;++i)
{
rdr[i]=(rdr[i>>1]>>1)|((i&1)<<(l-1));
if(i<rdr[i]) swap(a[i],a[rdr[i]]);
}
for(int i=1,p=0;i<n;i<<=1,++p)
{
int ww=op?W[p]:iW[p];
for(int j=0;j<n;j+=i<<1)
for(int k=0,w=1;k<i;++k,w=1ll*w*ww%mod)
y=1ll*a[j+k+i]*w%mod,a[j+k+i]=(a[j+k]-y+mod)%mod,a[j+k]=(a[j+k]+y)%mod;
}
if(!op) for(int i=0,w=ginv(n);i<n;++i) a[i]=1ll*a[i]*w%mod;
}
int aa[M],bb[M];
void sov(int l,int r)
{
if(l==r)
{
if(l>2) g[l]=(1ll*(l-2)*g[l-1]+g[l])%mod;
return;
}
int mid=(l+r)>>1;
sov(l,mid);
int l1=l>1?r+1-l:mid-l+1,len=1;
while(len<=l1+l1) len<<=1;
for(int i=max(l,3);i<=mid;++i) aa[i-l]=g[i];
for(int i=3;i<=l1;++i) bb[i]=1ll*g[i]*(i-2)%mod;
ntt(aa,len,1),ntt(bb,len,1);
for(int i=0;i<len;++i) aa[i]=1ll*aa[i]*bb[i]%mod;
ntt(aa,len,0);
for(int i=mid+1;i<=r;++i) g[i]=(g[i]+aa[i-l+1])%mod;
memset(aa,0,sizeof(int)*len),memset(bb,0,sizeof(int)*len);
if(l>1)
{
for(int i=max(l,3);i<=mid;++i) aa[i-l]=1ll*g[i]*(i-2)%mod;
for(int i=3;i<=l1;++i) bb[i]=g[i];
ntt(aa,len,1),ntt(bb,len,1);
for(int i=0;i<len;++i) aa[i]=1ll*aa[i]*bb[i]%mod;
ntt(aa,len,0);
for(int i=mid+1;i<=r;++i) g[i]=(g[i]+aa[i-l+1])%mod;
memset(aa,0,sizeof(int)*len),memset(bb,0,sizeof(int)*len);
}
sov(mid+1,r);
}
int main()
{
for(int i=1,p=0;p<16;i<<=1,++p)
W[p]=fpow(3,(mod-1)/(i<<1)),iW[p]=ginv(W[p]);
T=rd(),n=rd();
fac[0]=1;
g[1]=1,g[2]=2;
sov(1,n);
/*for(int i=3;i<=n;++i)
{
LL na=(LL)(i-2)*g[i-1];
for(int j=2;j<=i-3;++j)
na+=(LL)(i-j-2)*g[i-j]*g[j+1];
g[i]=na%mod;
}*/
while(T--)
{
for(int i=1;i<=n;++i) a[i]=rd();
bool ok=1;
memset(hd,0,sizeof(int)*(n+1)),tot=1;
ans=1,tp=0;
for(int i=n;i;--i)
{
while(tp&&st[tp]-a[st[tp]]+1>i) --tp;
if(tp) adde(st[tp],i),ok&=st[tp]-a[st[tp]]+1<=i-a[i]+1;
else if(i<n) ok=0;
st[++tp]=i;
}
if(!ok){puts("0");continue;}
dfs(n),printf("%d
",ans);
}
return 0;
}