P4566 [CTSC2018] 青蕈领主 题解
题目描述
对于一个排列 \(p\) ,记 \(l_i\) 为满足 \(p_{i-l_i+1},\cdots,p_i\) 在数轴上连续的最大 \(l_i\) (显然有\(1\le l_i\le i\))。
\(T\) 组询问,给定数组 \(l_i\) ,求有多少个排列 \(p\) 符合条件。
数据范围
- \(1\le T\le 100,1\le n\le 50000\) 。
- \(1\le l_i\le i\) 。
时间限制 \(\texttt{3s}\) ,空间限制 \(\texttt{500MB}\) 。
分析
\(\texttt{Key observation}\) :区间 \([i-l_i+1,i]\) 要么不交,要么包含!
证明:数轴上两个相交连续区间的并集仍是连续区间。
更进一步,所有 \([i-l_i+1,i]\) 的区间可以根据包含关系画成一棵树,并且根节点为 \([1,n]\) 。
至此可以给出无解条件:区间 \([i-l_i+1,i]\) 存在相交关系,或者 \(l_n\neq n\) 。
记 \(f_k\) 为长为 \(k+1\) 的 \(l\) 数组 1 ... 1 k+1 的答案。
对于节点 \(u\) ,假设它有 \(deg_u\) 个子节点,这一步我们只需决策这 \(deg_u\) 个区间的相对位置关系,使得每个区间都是极长连续区间。
显然不同步的决策相互独立,因此 \(ans=\prod_{i=1}^nf_{deg_i}\) 。
\(deg\) 可以用单调栈求出,接下来目标变为求 \(f_n\) 。
递推,在一个长为 \(n\) 的排列 \(p\) 中插入 \(n+1\) 。
如果 \(p\) 合法,只需保证 \(n+1\) 没有插在 \(n\) 旁边即可,方案数为 \((n-1)f_{n-1}\) 。
如果 \(p\) 不合法,并且插入 \(n+1\) 后变得合法,那么 \(p\) 中长度 \(\gt 1\) 的极长连续区间(不考虑 \([1,n]\),下同)恰好只能有一个,并且 \(n+1\) 必须插在这个区间内部。
假设这个区间长度为 \(i\) (\(1\lt i\le n-2\)),先将它缩成一个点,我们会得到一个长为 \(n-i+1\) ,并且除末尾元素外 \(l\) 全为 \(1\) 的排列 \(q\) ,这样的排列有 \(f_{n-i}\) 个。
接下来要做的事情是在值域 \([1,n-i+1]\) 上找一个点 \(x\) ,将其扩展成长为 \(i\) 的区间 \([x,x+i-1]\) ,然后插入 \(n+1\) 。
- 首先 \(x\) 不能是最大值 \(n-i+1\) ,否则 \([x,x+i-1]\cup\{n+1\}\) 仍是一个连续区间。
- 其次 \(x\) 不能是最后一个点 \(q_{n-i+1}\) ,因为我们已经默认前提删去 \(n+1\) 后排列不合法。
注意 \(q_{n-i+1}\neq n-i+1\) ,否则在 \(q\) 中删去最后一个值,\([1,n-i]\) 也是极长区间,与 \(q\) 的定义矛盾。
因此 \(x\) 有 \((n-i+1)-2=n-i-1\) 种取法,接下来考虑 \([x,x+i-1]\cup\{n+1\}\) 的排列方式。
重标号后等价于将 \([1,i+1]\) 进行排列,使得 \(l\) 数组为 1 ... 1 i+1 ,方案数为 \(f_i\) 。
综上所述:
初始值 \(f_0=1,f_1=2\) ,具体参见 OEIS A090753 。
半在线卷积?分治 \(\texttt{NTT}\) 准没错,但是具体过程仍然非常复杂。
模板题中 \(f_i=\sum f_jg_{i-j}\) ,其中 \(g\) 已知;但是本题 \(g_i=(i-1)f_i\) 仍然依赖 \(f\) 。
\(\texttt{cdq}\) 分治的本质是 \(f[l\sim mid]\times g[1\sim r-l]\to f[mid+1\sim r]\) ,如果 \(r-l\gt mid\) , \(g\) 数组未知,无法进行上述转移。
\(\texttt{Key observation}\) : \(r-l\gt mid\iff l=1\) ,即每次二分都往左走。
此时我们无法得到整个 \(g\) 数组,所以只能 \(f[l\sim mid]\times g[l\sim mid]\) 。
因此我们必须在 \(l\neq 1\) 时打补丁,除了常规的 \(f[l\sim mid]\times g[1\sim r-l]\) 以外,还要补上 \(f\) 下标小于 \(g\) 下标时的贡献,即 \(f[1\sim r-l]\times g[l\sim mid]\) (注意此时 \(r-l\le l\) 一定成立)。
时间复杂度\(O(Tn+n\log^2n)\) 。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1<<17,mod=998244353;
int n,t,x;
int f[maxn],u[maxn],st[maxn];
namespace Poly
{
int p[maxn],q[maxn],r[maxn],w[maxn];
int qpow(int a,int k)
{
int res=1;
for(;k;a=1ll*a*a%mod,k>>=1) if(k&1) res=1ll*res*a%mod;
return res;
}
int add(int x,int y)
{
if((x+=y)>=mod) x-=mod;
return x;
}
int dec(int x,int y)
{
if((x-=y)<0) x+=mod;
return x;
}
int extend(int n)
{
return n!=1?1<<(__lg(n-1)+1):1;
}
void get_r(int n)
{
for(int i=0;i<n;i++) r[i]=(r[i>>1]>>1)|(i&1?n>>1:0);
}
void init(int n=maxn)
{
for(int k=2,m=1;k<=n;k<<=1,m<<=1)
{
w[m]=1;
for(int i=m+1,x=qpow(3,(mod-1)/k);i<k;i++) w[i]=1ll*w[i-1]*x%mod;
}
}
void print(int *a,int n)
{
for(int i=0;i<n;i++) printf("%d%c",a[i]," \n"[i==n-1]);
}
void ntt(int *a,int n,int op)
{
for(int i=0;i<n;i++) if(i<r[i]) swap(a[i],a[r[i]]);
for(int k=2,m=1;k<=n;k<<=1,m<<=1)
for(int i=0;i<n;i+=k)
for(int j=i,*x=w+m;j<i+m;j++,x++)
{
int v=1ll*a[j+m]**x%mod;
a[j+m]=dec(a[j],v),a[j]=add(a[j],v);
}
if(op==-1)
{
reverse(a+1,a+n);
for(int i=0,v=qpow(n,mod-2);i<n;i++) a[i]=1ll*a[i]*v%mod;
}
}
void tran(int l1,int r1,int l2,int r2,int l3,int r3)
{
int n=r1-l1+1,m=r2-l2+1,len=extend(n+m-1);
for(int i=0;i<len;i++) p[i]=i<n?f[i+l1]:0,q[i]=i<m?(i+l2-1ll)*f[i+l2]%mod:0;
get_r(len),ntt(p,len,1),ntt(q,len,1);
for(int i=0;i<len;i++) p[i]=1ll*p[i]*q[i]%mod;
ntt(p,len,-1);
for(int i=max(l3,l1+l2);i<=min(r3,r1+r2);i++) f[i]=add(f[i],p[i-l1-l2]);
}
}
using Poly::tran;
void cdq(int l,int r)
{
if(l==r) return f[l]=(f[l]+(l-1ll)*f[l-1])%mod,void();
int mid=(l+r)>>1;
cdq(l,mid);
if(l==2) tran(l,mid,l,mid,mid+1,r);
else tran(l,mid,2,r-l,mid+1,r),tran(2,r-l,l,mid,mid+1,r);
cdq(mid+1,r);
}
int work()
{
for(int i=1;i<=n;i++) scanf("%d",&x),u[i]=i-x+1;
if(u[n]!=1) return 0;
int res=1;
for(int i=1,top=0;i<=n;i++)
{
int cnt=0;
while(top&&u[st[top]]>=u[i]) cnt++,top--;
if(top&&u[st[top]]<u[i]&&u[i]<=st[top]) return 0;
st[++top]=i,res=1ll*res*f[cnt]%mod;
}
return res;
}
int main()
{
scanf("%d%d",&t,&n),Poly::init();
f[0]=1,f[1]=2,cdq(2,n);
while(t--) printf("%d\n",work());
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/18689951
浙公网安备 33010602011771号