题目大意

\(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;
}

并不对劲的bzoj5342:loj2554:uoj401:p4566: [Ctsc2018]青蕈领主的更多相关文章

  1. [CTSC2018]青蕈领主

    [CTSC2018]青蕈领主 题解 首先,连续段要知道结论: 连续段要么不交,要么包含 所以是一棵树!每个位置的father是后面第一个包含它的 树形DP! 设dp[x],x为根的子树,(设管辖的区间 ...

  2. Loj #2554. 「CTSC2018」青蕈领主

    Loj #2554. 「CTSC2018」青蕈领主 题目描述 "也许,我的生命也已经如同风中残烛了吧."小绿如是说. 小绿同学因为微积分这门课,对"连续"这一概 ...

  3. bzoj5342 CTSC2018 Day1T3 青蕈领主

    首先显然的是,题中所给出的n个区间要么互相包含,要么相离,否则一定不合法. 然后我们可以对于直接包含的关系建出一棵树,于是现在的问题就是给n个节点分配权值,使其去掉最后一个点后不存在非平凡(长度大于1 ...

  4. UOJ#401. 【CTSC2018】青蕈领主 分治,FFT

    原文链接www.cnblogs.com/zhouzhendong/p/UOJ401.html 题解 首先,对于一个排列,它的连续段一定只有包含关系,没有相交关系. 我们可以据此得到一棵表示连续段的树. ...

  5. LOJ 2554 「CTSC2018」青蕈领主——结论(思路)+分治FFT

    题目:https://loj.ac/problem/2554 一个“连续”的区间必然是一个排列.所有 r 不同的.len 最长的“连续”区间只有包含.相离,不会相交,不然整个是一个“连续”区间. 只有 ...

  6. uoj#401. 【CTSC2018】青蕈领主(分治FFT)

    传送门 话说分治\(FFT\)是个啥子啊--还有题目里那字好像念(蕈xùn) 首先考虑无解的情况:区间相交或者\(L_n\neq n\) 这两个都可以感性理解一下 所以区间之间只会有包含关系,我们把每 ...

  7. 并不对劲的bzoj5341:loj2553:uoj400:p4565:[Ctsc2018]暴力写挂

    题目大意 有两棵\(n\)(\(n\leq366666\))个节点的树,\(T\)和\(T'\),有边权 \(dep(i)\)表示在\(T\)中\(i\)号点到\(1\)号点的距离,\(dep'(i) ...

  8. 并不对劲的bzoj5340:loj2552:uoj399:p4564: [Ctsc2018]假面

    题目大意 有\(n\)(\(n\leq200\))个非负整数\(m_1,m_2,...,m_n\)(\(\forall i\in[1,n],m_i\leq100\)),有\(q\)(\(q\leq2* ...

  9. CTSC2018 & APIO2018 颓废 + 打铁记

    CTSC2018 & APIO2018 颓废 + 打铁记 CTSC 5 月 6 日 完美错过报道,到酒店领了房卡放完行李后直接奔向八十中拿胸牌.饭票和资料.试机时是九省联考的题,从来没做过,我 ...

随机推荐

  1. CSUOJ 1256 天朝的单行道

    题目链接:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1256 题目大意:     在另一个平行宇宙中,有一个神奇的国度名叫天朝.天朝一共有N个城 ...

  2. SQLSERVER金额转换成英文大写的函数

    CREATE FUNCTION [dbo].[f_num_eng] (@num numeric(15,2)) RETURNS varchar(400) WITH ENCRYPTION AS BEGIN ...

  3. 约分差束 例题 ZOJ 2770 火烧连营

    题目来源:ZOJ Monthly, October 2006, ZOJ2770题目描述:大家都知道,三国时期,蜀国刘备被吴国大都督陆逊打败了.刘备失败的原因是刘备的错误决策.他把军队分成几十个大营,每 ...

  4. POJ 2438 哈密顿回路

    Children's Dining Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 4730   Accepted: 754 ...

  5. 转:String数组初始化

    近日,笔者在java编程中因为疏忽对String数组的初始化定义错误,导致程序运行出错.现将所理解的String数组在此进行说明,并对String数组初始化进行分析. //一维数组String[] s ...

  6. ModelAndView对象作用

    ModelAndView ModelAndView对象有两个作用: 作用一  :设置转向地址,如下所示(这也是ModelAndView和ModelMap的主要区别) ModelAndView mv = ...

  7. guava缓存设置return null一直报错空指针

    guava缓存设置return null一直报错空指针 因为缓存不允许返回为空

  8. SharePoint中取得ACL和组中用户数量

     SharePoint中取得ACL和组中用户数量 1. 取得ACL的数量: select COUNT(ra.PrincipalId) as [Count],p.ScopeUrl from [WSS_C ...

  9. IOS开发 序列化与反序列化

    原帖地址:http://blog.csdn.net/ally_ideveloper/article/details/7956942 不会用,记下自己有时间看 序列化与反序列化概述 序列化,它又称串行化 ...

  10. 【scrapy】创建第一个项目

    1)创建项目命令: scrapy startproject tutorial 该命令将在当前目录下创建tutorial文件夹 2)定义Item Items are containers that wi ...