题目大意

\(T\)(\(T\leq100\))组询问

有\(1\)到\(n\)(\(n\leq50000\))这\(n\)个整数组成的一个排列

定义这个排列的一个子区间是“连续”的,当且仅当这个子区间在位置上和在值域上都是连续的

分别给出这个排列以每个位置\(i\)为右端点的最长“连续”子区间的长度\(l_i\),问有多少个排列满足这个条件

题解

发现这些最长“连续”子区间一定是相互包含或相离的,不会相交

用反证法:假设有\(x<y\),\(x-l_x+1< y-l_y+1\)且\(y-l_y+1< x\),那么\(x-l_x+1\)到\(x\)这一段和\(y-l_y+1\)到\(y\)这一段在值域上连续,那么\(x-l_x+1\)到\(y-l_y+1\)、\(y-l_y+1\)到\(x\)、\(x\)到\(y\)这三段能拼成值域上连续的一段,这样\(x-l_x+1\)到\(y\)就是连续的,以\(y\)为右端点的最长“连续”子区间长度至少\(y-x+l_x\),显然\(y-x+l_x>l_y\),与题意不符

这样就可以将每个位置按以该位置为右端点的最长“连续”子区间的包含关系建成一棵树,而且判断无解也变得十分简单,当有相交的最长“连续”子区间或\(l_n\neq n\)时无解

建出树后,会发现对于每个点\(x\),\(x\)对应的区间相当于每个儿子对应的区间和位置\(x\)上的数拼成的

因为\(x\)的儿子对应的区间都是“连续”的,所以可以把每个儿子看成一个点,点\(x\)的子树的方案数就是所有儿子的方案数乘以把每个儿子看成一个点后满足某些性质的排列数

“满足某些性质的排列”指一些除了整个排列是“连续”的以外,任何一个长度大于\(1\)的子区间都不“连续”的排列,称满足该性质的排列是合法排列

设\(f_i\)表示长度为\(i+1\)的合法排列数

手算可得,\(f_0=1\),\(f_1=2\)

考虑递推求\(f_i\),即已有一个由\(1\)到\(i\)组成的排列,加入\(i+1\),使它合法

有这样两种可能:

一、\(1\)到\(i\)组成的排列合法

\(i+1\)只要不放到\(i\)旁边就行,\(1\)到\(i\)组成的排列一共有\(i+1\)个缝隙,去掉与\(i\)相邻的两个,还剩\(i-1\)个

所以这一部分是\((i-1)*f_{i-1}\)个

二、\(1\)到\(i\)组成的排列不合法

会发现加入\(i+1\)顶多将一个多余的“连续”子区间“打断”,那么就可以看成求有一个长度为\(i\)的排列,该排列只有一个长度大于\(1\)的“连续”子区间,且该子区间内部没有“连续”子区间(即该子区间合法),将\(i+1\)放到这个“连续”合法子区间内的一个位置把这个“连续”合法子区间“打断”

假设“连续”子区间长度为\(j\)

会发现这个问题非常不好解决,那么可以从结果来倒着想:

从这个排列中去掉\(i+1\)后,这个排列只有一个子区间是“连续”的,那么将该子区间缩成一个点,整个排列就变成了一个长度为\(i-j+1\)的合法排列

再把这个过程正过来,就变成了:

(1)生成一个长度为\(i-j+1\)的合法排列

(2)选择一个点,不能选\(i-j+1\)或\(i-j\)

(3)让被选的点变成一个长度为\(j\)的“连续”排列,再加入\(i+1\)这个数——会发现这个过程难以计算,那么还是从结果考虑——结果是一个长度为\(j+1\)的由\(1\)到\(j\)和\(i+1\)合法排列,它去掉\(i+1\)后一定是个长度为\(j\)的“连续”合法子区间(一个长度为\(j\)的由\(1\)到\(j\)的一定是“连续”的)——那么完全可以看成生成一个长度为\(j+1\)的合法排列

这个过程每一步的影响是:

(1)长度为\(i-j+1\)的合法排列数,\(f_{i-j}\)

(2)在\(i-j-1\)个数中选一个,\(i-j-1\)

(3)长度为\(j+1\)的合法排列数,\(f_j\)

在加入\(i+1\)前,“连续”子区间长度为\(j\)的长度为\(i+1\)的合法排列数为\((i-j-1)*f_j*f_{i-j}\)

\(j\)的取值范围是多少呢?

要想使\(1\)到\(i\)组成的排列不合法,多余的“连续”子区间的长度\(j\)至少为\(2\)

要想使\(i-j-1\)个可选的数中至少有一个,\(i-j-1\geq1\),即\(j\leq i-2\)

所以这一部分是$$\sum_{j=2}^{i-2}{(i-j-1)f_jf_{i-j}}$$

这两部分一共有$$f_i=(i-1)f_{i-1}+\sum_{j=2}^{i-2}{(i-j-1)f_j*f_{i-j}}$$

现在想必是可以进行\(\Theta(n^2)\)的递推了,能否进行优化呢?

后面的部分分治FFT解决,前面的部分在分治到左端点等于右端点时直接加到下一项就行

这样就完了?想必没有

会发现在计算完\([l,m]\)这一段后,可能会出现\(i\in[m+1,r],j\in[l,m]\),使\(i-j\in[m+1,r]\),显然此时\(f_{i-j}\)的值还未算出来

这时就得把后面的部分拆成两部分:

\[\sum_{j=2}^{i-2}{(i-j-1)*f_j*f_{i-j}}=\sum_{j=2}^{\frac{i-2}{2}}(i-j-1)*f_j*f_{i-j}+\sum_{j=2}^{\frac{i-2}{2}}(j-1)*f_j*f_{i-j}
\]

这就能卷了吧?

#include<algorithm>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#define rep(i,x,y) for(register int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(register int i=(x);i>=(y);--i)
#define maxn 50010
#define maxlen (maxn<<2)
#define LL long long
#define mi (L+R>>1)
#define lim 80
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
void write(int x)
{
if(x==0){putchar('0'),putchar('\n');return;}
int f=0;char ch[20];
if(x<0)putchar('-'),x=-x;
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar('\n');
return;
}
int a[maxlen],b[maxlen],c[maxlen],r[maxlen],lena,lenb,lenc,len,f[maxn],no;
int t,n,l[maxn];
const LL mod=998244353;
int mul(int x,int y){int ans=1;while(y){if(y&1)ans=(LL)ans*(LL)x%mod;x=(LL)x*(LL)x%mod;y>>=1;}return ans;}
void dnt(int *u,int f)
{
rep(i,1,lenc)if(i<r[i])swap(u[i],u[r[i]]);
for(int i=1;i<lenc;i<<=1)
{
int wn=mul(3,(mod-1)/(i<<1));
if(f==-1)wn=mul(wn,mod-2);
for(int j=0;j<lenc;j+=(i<<1))
{
int w=1,x,y;
rep(k,0,i-1)
x=u[j+k],y=(LL)w*(LL)u[i+j+k]%mod,u[j+k]=(x+y)%mod,u[j+i+k]=(x-y+mod)%mod,w=(LL)w*(LL)wn%mod;
}
}
if(f==-1)
{
int inv=mul(lenc,mod-2);
rep(i,0,lenc-1)u[i]=(LL)u[i]*(LL)inv%mod;
}
}
void work(int L,int R)
{
if(L==R)
{
if(L==1)return;
f[L]=(f[L]+(LL)f[L-1]*(LL)(L-1)%mod)%mod;
f[L]=(f[L]-(LL)(L-2)*(LL)f[L-1]%mod*(LL)f[1]%mod+mod)%mod;
return;
}
work(L,mi);
if(R-L<=lim)
{
rep(i,mi+1,R)
{
rep(j,i-mi,min(mi,i-L))
f[i]=((LL)f[i]+(LL)f[j]*(LL)f[i-j]%mod*(LL)(j-1)%mod)%mod;
rep(j,i-mi,min(i-L,L-1))
f[i]=((LL)f[i]+(LL)f[j]*(LL)f[i-j]%mod*(LL)(i-j-1)%mod)%mod;
}
}
else
{
len=lena=lenb=0;lenc=1;
rep(i,1,min(R-L,mi))a[lena++]=(LL)(i-1)*(LL)f[i]%mod;
rep(i,L,mi)b[lenb++]=f[i];
while(lenc<lena+lenb-1)lenc<<=1,len++;
rep(i,1,lenc)r[i]=(r[i>>1]>>1)|((i&1)<<(len-1));
dnt(a,1),dnt(b,1);
rep(i,0,lenc-1)c[i]=(LL)a[i]*(LL)b[i]%mod;
dnt(c,-1);
rep(i,mi+1,R)f[i]=(f[i]+c[i-L-1])%mod;
rep(i,0,lenc)a[i]=b[i]=c[i]=0;
len=lena=lenb=0;lenc=1;
rep(i,1,min(R-L,L-1))a[lena++]=f[i];
rep(i,L,mi)b[lenb++]=(LL)(i-1)*(LL)f[i]%mod;
while(lenc<lena+lenb-1)lenc<<=1,len++;
rep(i,1,lenc)r[i]=(r[i>>1]>>1)|((i&1)<<(len-1));
dnt(a,1),dnt(b,1);
rep(i,0,lenc-1)c[i]=(LL)a[i]*(LL)b[i]%mod;
dnt(c,-1);
rep(i,mi+1,R)f[i]=(f[i]+c[i-L-1])%mod;
rep(i,0,lenc)a[i]=b[i]=c[i]=0;
}
work(mi+1,R);
}
int getans(int L,int R)
{
int ans=1,nums=0;
dwn(i,R-1,L)
{
int x=i-l[i]+1;
if(x<L){no=1;return 0;}
int tmp=getans(x,i);
if(no==1)return 0;
ans=(LL)ans*(LL)tmp%mod;
nums++; i=x;
}
ans=(LL)ans*(LL)f[nums]%mod;
return ans;
}
int main()
{
t=read(),n=read();
f[0]=1;
f[1]=2;
work(2,n);
while(t--)
{
no=0;
rep(i,1,n)l[i]=read();
if(l[n]!=n){puts("0");continue;}
int ans=getans(1,n);
write(ans);
}
return 0;
}

最新文章

  1. Navicat备份远程Oracle数据库到本地
  2. 使用Azure Blob存储
  3. HDU 5920 Ugly Problem
  4. GCD与NSOperationQueue
  5. QueryRunner(common-dbutils.jar)
  6. ruby 删除文件
  7. ArcBruTile 0.2.2
  8. NYOJ-235 zb的生日 AC 分类: NYOJ 2013-12-30 23:10 183人阅读 评论(0) 收藏
  9. 位图文件格式及linux下C语言来操作位图文件
  10. MySQL操作备忘录
  11. 10个android开源项目
  12. post提交与get提交的一个小知识点
  13. MVC传递Model之TempData、ViewData、ViewBag差别及用途
  14. 关于block 用法
  15. Qt 共享库(动态链接库)和静态链接库的创建及调用
  16. [Swift]LeetCode226. 翻转二叉树 | Invert Binary Tree
  17. 安装VMware tools
  18. Maven打包相关插件集合
  19. Jlink使用技巧之读取STM32内部的程序
  20. Git clone 报错 Unable to negotiate with xxx.xxx.xxx.xxx port 12345: no matching cipher found. Their offer: aes128-cbc,3des-cbc,blowfish-cbc

热门文章

  1. Android版网易云音乐唱片机唱片磁盘旋转及唱片机机械臂动画关键代码实现思路
  2. 【ITOO 2】.NET 动态建库建表:使用SQL字符串拼接方式
  3. Android: java.lang.ClassCastException: android.widget.imageView cannot be cast to android.widget.textView异常解决
  4. POJ 1741 树上 点的 分治
  5. 一份关于webpack2和模块打包的新手指南(二)
  6. P2839 畅通工程
  7. Angular结构简单介绍
  8. Atomic Builtins - Using the GNU Compiler Collection (GCC) GCC 提供的原子操作
  9. 使用vscode 编译 sass
  10. POJ训练计划2299_Ultra-QuickSort(线段树/单点更新)