原文链接www.cnblogs.com/zhouzhendong/p/UOJ401.html

题解

首先,对于一个排列,它的连续段一定只有包含关系,没有相交关系。

我们可以据此得到一棵表示连续段的树。

对于一个连续段节点,它有若干儿子。

由于它的每一个儿子都是连续段,所以我们可以将这些儿子各自看作一个数。设节点x的度数为 d[x]。

设 f[x] 表示 L 数组为 1,1,1,...1,L+1 这样的排列个数,那么答案就是 $\prod f[d[x]]$ 。

然后我们得到了一个关于 f[x] 的迷之式子:

$$f[i] = (i-1) \cdot f[i-1]  + \sum_{j=2}^{i-2} (j-1) \cdot f[j] \cdot f[i-j]$$

然后我们考虑对这个东西做分治FFT来求。

这个分治 FFT 的大致流程如下:

求解区间 [L,R] 时,如果 L = R,那么分治结束。

否则,设 mid = (L+R)/2,先 solve(L,mid) 。

然后考虑计算 [L,mid] 对 [mid+1,R] 的贡献。

这时需要分类讨论:

若 L = 1,那么直接 FFT

若 L > 1,那么需要计算两部分贡献:

  1.  $j\in [2,R-L],i-j\in[L,mid]$

  2.  $i-j\in [2,R-L], j\in[L,mid]$

各自FFT即可。

时间复杂度 $O(n\log^2 n)$ 。

代码

#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof x)
#define For(i,a,b) for (int i=a;i<=b;i++)
#define Fod(i,b,a) for (int i=b;i>=a;i--)
#define fi first
#define se second
#define pb(x) push_back(x)
#define mp(x,y) make_pair(x,y)
#define outval(x) printf(#x" = %d\n",x)
#define outtag(x) puts("---------------"#x"---------------")
#define outarr(a,L,R) printf(#a"[%d..%d] = ",L,R);\
For(_x,L,R)printf("%d ",a[_x]);puts("")
using namespace std;
typedef long long LL;
LL read(){
LL x=0,f=0;
char ch=getchar();
while (!isdigit(ch))
f|=ch=='-',ch=getchar();
while (isdigit(ch))
x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return f?-x:x;
}
const int N=1<<17,mod=998244353;
int Pow(int x,int y){
int ans=1;
for (;y;y>>=1,x=(LL)x*x%mod)
if (y&1)
ans=(LL)ans*x%mod;
return ans;
}
void Add(int &x,int y){
if ((x+=y)>=mod)
x-=mod;
}
void Del(int &x,int y){
if ((x-=y)<0)
x+=mod;
}
int Del(int x){
return x<0?x+mod:x;
}
namespace fft{
int w[N],R[N];
void init(int n){
int d=0;
while ((1<<d)<n)
d++;
For(i,0,n-1)
R[i]=(R[i>>1]>>1)|((i&1)<<(d-1));
w[0]=1,w[1]=Pow(3,(mod-1)/n);
For(i,2,n-1)
w[i]=(LL)w[i-1]*w[1]%mod;
}
void FFT(int *a,int n,int flag){
if (flag<0)
reverse(w+1,w+n);
For(i,0,n-1)
if (i<R[i])
swap(a[i],a[R[i]]);
for (int t=n>>1,d=1;d<n;d<<=1,t>>=1)
for (int i=0;i<n;i+=d<<1)
for (int j=0;j<d;j++){
int tmp=(LL)w[t*j]*a[i+j+d]%mod;
a[i+j+d]=Del(a[i+j]-tmp);
Add(a[i+j],tmp);
}
if (flag<0){
reverse(w+1,w+n);
int inv=Pow(n,mod-2);
For(i,0,n-1)
a[i]=(LL)a[i]*inv%mod;
}
}
}
using fft::FFT;
namespace pre{
int f[N];
int a[N],b[N];
void solve(int L,int R){
if (L==R){
f[L]=((LL)f[L-1]*(L-1)+f[L])%mod;
return;
}
int mid=(L+R)>>1;
solve(L,mid);
if (L>1){
int n=1;
while (n<(mid-L+1)+(R-L))
n<<=1;
fft::init(n);
memset(a,0,n<<2),memset(b,0,n<<2);
For(i,L,mid)
a[i-L]=f[i];
For(i,2,R-L)
b[i]=(LL)(i-1)*f[i]%mod;
FFT(a,n,1),FFT(b,n,1);
For(i,0,n-1)
a[i]=(LL)a[i]*b[i]%mod;
FFT(a,n,-1);
For(i,mid+1,R)
Add(f[i],a[i-L]);
memset(a,0,n<<2),memset(b,0,n<<2);
For(i,L,mid)
a[i-L]=(LL)(i-1)*f[i]%mod;
For(i,2,R-L)
b[i]=f[i];
FFT(a,n,1),FFT(b,n,1);
For(i,0,n-1)
a[i]=(LL)a[i]*b[i]%mod;
FFT(a,n,-1);
For(i,mid+1,R)
Add(f[i],a[i-L]);
}
else {
int n=1;
while (n<mid*2+1)
n<<=1;
memset(a,0,n<<2),memset(b,0,n<<2);
For(i,2,mid)
a[i]=f[i];
For(i,2,mid)
b[i]=(LL)(i-1)*f[i]%mod;
fft::init(n);
FFT(a,n,1),FFT(b,n,1);
For(i,0,n-1)
a[i]=(LL)a[i]*b[i]%mod;
FFT(a,n,-1);
For(i,mid+1,R)
Add(f[i],a[i]);
}
solve(mid+1,R);
}
void prework(int n){
f[0]=1,f[1]=2;
solve(1,n);
// For(i,2,n){
// f[i]=(LL)f[i-1]*(i-1)%mod;
// For(j,2,i-2)
// Add(f[i],(LL)(j-1)*f[j]%mod*f[i-j]%mod);
// }
//1 2 2 4 16 88 600 4800 43680 443296 4949920 60217408 792134528
}
}
int T,n;
int a[N];
void Solve(){
For(i,1,n)
a[i]=read();
if (a[n]!=n)
return (void)(puts("0"));
int ans=1;
For(i,1,n){
int c=0,j;
for (j=i-1;j>i-a[i];j-=a[j])
c++;
if (j<i-a[i])
return (void)(puts("0"));
ans=(LL)ans*pre::f[c]%mod;
}
printf("%d\n",ans);
}
int main(){
pre::prework(50000);
T=read(),n=read();
while (T--)
Solve();
return 0;
}

  

UOJ#401. 【CTSC2018】青蕈领主 分治,FFT的更多相关文章

  1. [CTSC2018]青蕈领主

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

  2. 并不对劲的bzoj5342:loj2554:uoj401:p4566: [Ctsc2018]青蕈领主

    题目大意 \(T\)(\(T\leq100\))组询问 有\(1\)到\(n\)(\(n\leq50000\))这\(n\)个整数组成的一个排列 定义这个排列的一个子区间是"连续" ...

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

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

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

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

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

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

  6. bzoj5342 CTSC2018 Day1T3 青蕈领主

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

  7. 【UOJ#50】【UR #3】链式反应(分治FFT,动态规划)

    [UOJ#50][UR #3]链式反应(分治FFT,动态规划) 题面 UOJ 题解 首先把题目意思捋一捋,大概就是有\(n\)个节点的一棵树,父亲的编号大于儿子. 满足一个点的儿子有\(2+c\)个, ...

  8. 再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Bluestein算法+分治FFT+FFT的优化+任意模数NTT)

    再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Bluestein算法+分治FFT+FFT的优化+任意模数NTT) 目录 再探快速傅里叶变换(FFT)学习笔记(其三)(循环卷积的Blueste ...

  9. BNUOJ 51279[组队活动 Large](cdq分治+FFT)

    传送门 大意:ACM校队一共有n名队员,从1到n标号,现在n名队员要组成若干支队伍,每支队伍至多有m名队员,求一共有多少种不同的组队方案.两个组队方案被视为不同的,当且仅当存在至少一名队员在两种方案中 ...

随机推荐

  1. ADO.NET 四(DataReader)

    DataReader 类概述 DataReader 类对应MSSQLSERVER在 System.Data.SqlClient 命名空间中,对应的类是 SqlDataReader,主要用于读取表中的查 ...

  2. Vue 使用技巧手记

    watch监听 Vue监听属性有很多种写法,也有几个配置选项 使用配置项 new Vue({ watch:{ content:{ handler(old,newVal){ console.log(ol ...

  3. jQuery常用知识点大总结

    目录 jQuery jQuery介绍 jQuery的优势 jQuery的引入方式有两种: jQuery对象和dom对象 jQuery选择器 基本选择器(同css) 基本筛选器(选择之后进行过滤): 属 ...

  4. 73.移动端Web界面滚动性能优化 Passive event listeners

    题,滚动时候不仅滚动了希望滚动的部分,整体的页面也跟随者上下滚动,整个页面非常卡顿. 这两个页面都用了touch事件 控制台打印如下警告: [Intervention] Unable to preve ...

  5. NSLog的各种打印格式符和打印CGRect相关结构体

    1.打印CG开头的数据类型,如CGRect,CGSize等方法 1.1 打印CGRect : NSLog(@"%@", NSStringFromCGRect(someCGRect) ...

  6. 在Activity/Fragment以外使用Toast【转】

    在 Activity 使用 Toast 这种是最基本的使用,Toast的第一个参数就是Context,一般在Activity中我们直接用this代替,代表调用者的实例为Activity. public ...

  7. [ipsec][strongswan] VirtualPN隧道网络加速FEC(forward error correction)

    引用 跟一个网友就有关IPsec的网络加速以及降低延迟等问题进行了一些讨论,并总结了一写粗浅的看法. 因为FEC的资料并不多,所以分享出来,希望能被有需要的人看见:) 先说一下FEC. 我们使用ips ...

  8. 【HCIA Gauss】学习汇总-数据库基础介绍-1

    存放在数据库中数据的特点 :永久存储 又组织 可共享 数据库系统是由 数据库 数据库管理系统 应用程序 管理员成员 组成的存储 管理 处理和维护数据的系统 三个阶段:人工阶段 ,文件系统阶段,数据库系 ...

  9. onvirt安装linux系统

    情况说明: (1)本文接前文kvm虚拟化学习笔记(十九)之convirt集中管理平台搭建,采用convirt虚拟化平台安装linux操作系统的过程,这个过程中需要对convirt进行一系列的配置才能真 ...

  10. 根据byte数组,生成文件

    /** * 根据byte数组,生成文件 * filePath 文件路径 * fileName 文件名称(需要带后缀,如*.jpg.*.java.*.xml) */ public static void ...