Loj #2554. 「CTSC2018」青蕈领主

题目描述

“也许,我的生命也已经如同风中残烛了吧。”小绿如是说。

小绿同学因为微积分这门课,对“连续”这一概念产生了浓厚的兴趣。小绿打算把连续的概念放到由整数构成的序列上,他定义一个长度为 \(m\) 的整数序列是连续的,当且仅当这个序列中的最大值与最小值的差,不超过\(m-1\)。例如 \(\{1,3,2\}\) 是连续的,而 \(\{1,3\}\) 不是连续的。

某天,小绿的顶头上司板老大,给了小绿 \(T\) 个长度为 \(n\) 的排列。小绿拿到之后十分欢喜,他求出了每个排列的每个区间是否是他所定义的“连续”的。然而,小绿觉得被别的“连续”区间包含住的“连续”区间不够优秀,于是对于每个排列的所有右端点相同的“连续”区间,他只记录下了长度最长的那个“连续”区间的长度。也就是说,对于板老大给他的每一个排列,他都只记录下了在这个排列中,对于每一个 \(1 \le i \le n\),右端点为 \(i\) 的最长“连续”区间的长度 \(L_i\)。显然这个长度最少为 \(1\),因为所有长度为 \(1\) 的整数序列都是连续的。

做完这一切后,小绿爬上绿色床,美美地做了一个绿色的梦。

可是第二天醒来之后,小绿惊讶的发现板老大给他的所有排列都不见了,只剩下他记录下来的 \(T\) 组信息。小绿知道自己在劫难逃,但是作为一个好奇的青年,他还是想知道:对于每一组信息,有多少个和信息符合的长度为 \(n\) 的排列。

由于小绿已经放弃治疗了,你只需要告诉他每一个答案对 \(998244353\) 取模的结果。

我们并不保证一定存在至少一个符合信息的排列,因为小绿也是人,他也有可能犯错。

输入格式

输入的第一行包含两个整数 \(T,n\),分别表示板老大给小绿的排列个数、以及每个排列的长度。

接下来 \(T\) 行,每行描述一组信息,包含 \(n\) 个正整数,第 \(i\) 组信息的从左往右第 \(j\) 个整数 \(L_{i,j}\) 表示第 \(i\) 个排列中右端点为第 \(j\) 个数的最长“连续”区间的长度。

对于每一行,如果行内包含多个数,则用单个空格将它们隔开。

输出格式

对于每组信息,输出一行一个整数表示可能的排列个数对 \(998244353\) 取模的结果。由于是计算机帮你算,所以我们不给你犯错的机会。

数据范围与提示

对于所有测试数据,\(1 \le T \le 100\),\(1 \le N \le 50000\), \(1 \le L_{i,j} \le j\)。

首先我们得到很多段区间,这些区间要么相离,要么包含,并且一定有一个\([1,n]\)。就用这两个条件来判断无解。然后我们可以将这些区间建成一个树。

考虑区间\([l,r]\),他有\(k\)个儿子,于是我们要将一段长为\(r-l+1\)的连续区间分配个这\(k\)个儿子。首先这\(k\)个儿子每一个都是连续的一段,并且相邻的儿子不能组成连续的一段。但是考虑放在\(r\)位置上的那个点,他与所有儿子共同组成了连续的一段。如果说我们把每个儿子看做一个点而,再把\(r\)也看做一个点,那么合法条件就是:一个\(k+1\)的排列,不能存在不包含最后一个位置的长度\(>1\)的连续区间。

设该答案为\(f_k\),那么:

\[f_{n}=(n-1)f_{n-1}+\sum_{j=2}^{n-2}(j-1)f_jf_{i-j}\\
\]

特别地,\(f_0=1,f_1=2\)。

我们设一个合法数列为\(A\),再令\(b_{a_i}=i\)。很容易发现\(A\)与\(B\)是唯一映射的。\(A\)的合法条件在\(B\)数组中等价为不能存在不包含最大那个元素的长度\(\geq 2\)的连续区间。

考虑从大到小插入每一个数。已经插入了\([2,n+1]\),现在要插入\(1\)。如果原来的序列已经合法,那么\(1\)只要不与\(2\)相邻,这个数列依旧合法。这样就还有\(n-1\)个位置可以插入,所以有\((n-1)f_{n-1}\)。如果原来的数列不合法,那么我们要插入\(1\)破坏那个长度\(>1\)的连续区间。很显然,不相交的连续区间最多有一个,不然插入一个\(1\)解决不了问题。于是我们枚举最大的那个非法区间的长度,设其为\(j\),则\(2\leq j\leq n-2\)(首先至少有\(2\)两个元素,并且不能让最大的那个元素单独存在,否则就不是最长的了)。假设非法区间的元素为\([x\ldots x+j-1]\),那么\(x\)有\([2,n-j]\)这\(n-j-1\)种方案,所以乘上系数\(n-1-j\)。考虑将\(1\)插入其中,等价于将\(j+1\)插入\([1,j]\)中,所以合法方案数为\(f_j\)。考虑这段连续区间(插入\(1\)之前)必须是极长,所以如果将这段连续区间视为一个点,那么就有还有\(n-j\)个元素,合法的排列方案数是\(f_{n-j}\)。这部分贡献为

\[\sum_{j=2}^{n-2}(n-j-1)f_jf_{n-j}=\sum_{j=2}^{n-2}(j-1)f_jf_{n-j}
\]

发现这个方程可以用分治\(FFT\)优化,不过这个写法有点巧妙。

考虑分治\(FFT\)的原理是递归区间\([l,r]\)的时候将区间分为了两半,考虑计算左半边对右半边的贡献。于是我们发现,计算\([l,mid]\)对\([mid+1,r]\)的贡献时,我们要用到\(f_{2\ldots r-l}\),但是可能\(mid<r-l\)。我们考虑\(i,j(i<j)\),显然只会在递归到某一个区间\([l,r]\)的时候才会计算\(i\)对\(j\)的贡献。假设这个区间是\([l,mid,r]\),如果\(l\leq r-l\leq mid\),那么用\(f_{l\ldots mid}\)自己卷自己就行了。如果\(r-l<l\),那么我们用\(f_{l\ldots mid}\)卷\(f_{2\ldots \min\{r-l,l-1\}}\)就好了。因为:

\[(j-1)f_jf_{i-j}+(i-j-1)f_{i-j}f_j=(i-2)f_jf_{i-j}
\]

所以对于计算了\(r-l<l\)的部分后乘上\(i-2\)就可以一并计算\(r-l>mid\)的部分了。

代码:

#include<bits/stdc++.h>
#define ll long long
#define N 50005 using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;} const ll mod=998244353;
ll ksm(ll t,ll x) {
ll ans=1;
for(;x;x>>=1,t=t*t%mod)
if(x&1) ans=ans*t%mod;
return ans;
} int n;
int p[N];
void NTT(ll *a,int d,int flag) {
static int rev[N<<2];
static ll G=3;
int n=1<<d;
for(int i=0;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<d-1);
for(int i=0;i<n;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int s=1;s<=d;s++) {
int len=1<<s,mid=len>>1;
ll w=flag==1?ksm(G,(mod-1)/len):ksm(G,mod-1-(mod-1)/len);
for(int i=0;i<n;i+=len) {
ll t=1;
for(int j=0;j<mid;j++,t=t*w%mod) {
ll u=a[i+j],v=a[i+j+mid]*t%mod;
a[i+j]=(u+v)%mod;
a[i+j+mid]=(u-v+mod)%mod;
}
}
}
if(flag==-1) {
ll inv=ksm(n,mod-2);
for(int i=0;i<n;i++) a[i]=a[i]*inv%mod;
}
} ll f[N];
ll A[N<<2],B[N<<2]; void solve(int l,int r) {
if(l==r) {
(f[l]+=f[l-1]*(l-1))%=mod;
return ;
}
int mid=l+r>>1;
int d=ceil(log2(r-l+1));
solve(l,mid);
for(int i=0;i<1<<d;i++) A[i]=B[i]=0;
for(int i=l;i<=mid;i++) {
A[i-l]=(i-1)*f[i]%mod;
B[i-l]=f[i];
}
NTT(A,d,1),NTT(B,d,1);
for(int i=0;i<1<<d;i++) A[i]=A[i]*B[i]%mod;
NTT(A,d,-1);
for(int i=0;i<1<<d;i++)
if(mid<i+2*l&&i+2*l<=r) (f[i+2*l]+=A[i])%=mod; int len=min(r-l,l-1);
d=ceil(log2(len+mid-l+1));
for(int i=0;i<1<<d;i++) A[i]=B[i]=0;
for(int i=l;i<=mid;i++) A[i-l]=f[i];
for(int i=2;i<=len;i++) B[i-2]=f[i];
NTT(A,d,1),NTT(B,d,1);
for(int i=0;i<1<<d;i++) A[i]=A[i]*B[i]%mod;
NTT(A,d,-1);
for(int i=0;i<1<<d;i++)
if(mid<i+2+l&&i+2+l<=r) (f[i+2+l]+=(i+l)*A[i])%=mod;
solve(mid+1,r);
} int st[N],top;
int sn[N]; int main() {
int T=Get();
n=Get();
f[0]=1,f[1]=2;
if(n-1>2) solve(2,n-1);
while(T--) {
for(int i=1;i<=n;i++) p[i]=i-Get()+1;
for(int i=1;i<=n;i++) sn[i]=0;
if(p[n]!=1) {
cout<<0<<"\n";
continue ;
}
int flag=0;
st[top=1]=n;
for(int i=n-1;i>=1;i--) {
while(p[st[top]]>i) top--;
sn[st[top]]++;
if(p[st[top]]>p[i]) {
flag=1;
break;
}
st[++top]=i;
}
if(flag) {
cout<<0<<"\n";
} else {
ll ans=1;
for(int i=1;i<=n;i++) ans=ans*f[sn[i]]%mod;
cout<<ans<<"\n";
}
}
return 0;
}

Loj #2554. 「CTSC2018」青蕈领主的更多相关文章

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

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

  2. loj#2552. 「CTSC2018」假面

    题目链接 loj#2552. 「CTSC2018」假面 题解 本题严谨的证明了我菜的本质 对于砍人的操作好做找龙哥就好了,blood很少,每次暴力维护一下 对于操作1 设\(a_i\)为第i个人存活的 ...

  3. Loj #2553. 「CTSC2018」暴力写挂

    Loj #2553. 「CTSC2018」暴力写挂 题目描述 temporaryDO 是一个很菜的 OIer .在 4 月,他在省队选拔赛的考场上见到了<林克卡特树>一题,其中 \(k = ...

  4. LOJ 2553 「CTSC2018」暴力写挂——边分治+虚树

    题目:https://loj.ac/problem/2553 第一棵树上的贡献就是链并,转化成 ( dep[ x ] + dep[ y ] + dis( x, y ) ) / 2 ,就可以在第一棵树上 ...

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

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

  6. LOJ 2557 「CTSC2018」组合数问题 (46分)

    题目:https://loj.ac/problem/2557 第一个点可以暴搜. 第三个点无依赖关系,k=3,可以 DP .dp[ cr ][ i ][ j ] 表示前 cr 个任务.第一台机器最晚完 ...

  7. LOJ 2555 「CTSC2018」混合果汁——主席树

    题目:https://loj.ac/problem/2555 二分答案,在可以选的果汁中,从价格最小的开始选. 按价格排序,每次可以选的就是一个前缀.对序列建主席树,以价格为角标,维护体积和.体积*价 ...

  8. LOJ 2552 「CTSC2018」假面——DP

    题目:https://loj.ac/problem/2552 70 分就是 f[i][j] 表示第 i 个人血量为 j 的概率.这部分是 O( n*Q ) 的:g[i][j][0/1] 表示询问的人中 ...

  9. bzoj5342 CTSC2018 Day1T3 青蕈领主

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

随机推荐

  1. 安装torch

    一.实验环境 1.Windows7x64_SP1 2.anaconda3.7 + python3.7(anaconda集成,不需单独安装) 二.问题描述 1.使用如下命令进行安装: pip3 inst ...

  2. 源码学习之Spring (系统架构简单解析)

    Spring Framework 系统架构总览图 Spring Framework的模块依赖关系图 Spring Framework各个模块功能说明 Spring核心模块 模块名称 主要功能 Spri ...

  3. 感受一把面试官通过一道题目引出的关于 TCP 的 5 个连环炮!

    面试现场:从 URL 在浏览器被被输入到页面展现的过程中发生了什么? 相信大多数准备过的同学都能回答出来,但是如果继续问:收到的 HTML 如果包含几十个图片标签,这些图片是以什么方式什么顺序下载?建 ...

  4. pandas 学习 第7篇:DataFrame - 数据处理(应用、操作索引、重命名、合并)

    DataFrame的这些操作和Series很相似,这里简单介绍一下. 一,应用和应用映射 apply()函数对每个轴应用一个函数,applymap()函数对每个元素应用一个函数: DataFrame. ...

  5. MapReduce Shuffle 和 Spark Shuffle 原理概述

    Shuffle简介 Shuffle的本意是洗牌.混洗的意思,把一组有规则的数据尽量打乱成无规则的数据.而在MapReduce中,Shuffle更像是洗牌的逆过程,指的是将map端的无规则输出按指定的规 ...

  6. Python笔记:设计模式之命令模式

    命令模式,正如模式的名字一样,该模式中的不同操作都可以当做不同的命令来执行,可以使用队列来执行一系列的命令,也可以单独执行某个命令.该模式重点是将不同的操作封装为不同的命令对象,将操作的调用者与执行者 ...

  7. SSM框架之Mybatis(3)dao层开发

    Mybatis(3)dao层开发 以实现类完成CRUD操作 1.持久层dao层接口的书写 src\main\java\dao\IUserDao.java package dao; import dom ...

  8. HTML5新标签与特性---新表单+新属性----综合案例1

    HTML5新标签与特性 兼容性问题 (ie9 以上的版本) 文档类型设定 document HTML: sublime 输入 html:4s XHTML: sublime 输入 html:xt HTM ...

  9. 从《华为的冬天》到AI的冬天 | 甲子光年

    知难不难,惶者生存. 作者 | DougLong 编辑 | 火柴Q.甲小姐 *本文为甲子光年专栏作家DougLong独家稿件.作者为AI从业者.Gary Marcus<Rebooting AI& ...

  10. python从入门到放弃之线程篇

    一,什么是多线程? 1.多线程的概念? 说起多线程,那么就不得不说什么是线程,而说起线程,又不得不说什么是进程. 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分 ...