并不对劲的bzoj5342:loj2554:uoj401:p4566: [Ctsc2018]青蕈领主
题目大意
\(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}\)的值还未算出来
这时就得把后面的部分拆成两部分:
\]
这就能卷了吧?
#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]青蕈领主的更多相关文章
- [CTSC2018]青蕈领主
[CTSC2018]青蕈领主 题解 首先,连续段要知道结论: 连续段要么不交,要么包含 所以是一棵树!每个位置的father是后面第一个包含它的 树形DP! 设dp[x],x为根的子树,(设管辖的区间 ...
- Loj #2554. 「CTSC2018」青蕈领主
Loj #2554. 「CTSC2018」青蕈领主 题目描述 "也许,我的生命也已经如同风中残烛了吧."小绿如是说. 小绿同学因为微积分这门课,对"连续"这一概 ...
- bzoj5342 CTSC2018 Day1T3 青蕈领主
首先显然的是,题中所给出的n个区间要么互相包含,要么相离,否则一定不合法. 然后我们可以对于直接包含的关系建出一棵树,于是现在的问题就是给n个节点分配权值,使其去掉最后一个点后不存在非平凡(长度大于1 ...
- UOJ#401. 【CTSC2018】青蕈领主 分治,FFT
原文链接www.cnblogs.com/zhouzhendong/p/UOJ401.html 题解 首先,对于一个排列,它的连续段一定只有包含关系,没有相交关系. 我们可以据此得到一棵表示连续段的树. ...
- LOJ 2554 「CTSC2018」青蕈领主——结论(思路)+分治FFT
题目:https://loj.ac/problem/2554 一个“连续”的区间必然是一个排列.所有 r 不同的.len 最长的“连续”区间只有包含.相离,不会相交,不然整个是一个“连续”区间. 只有 ...
- uoj#401. 【CTSC2018】青蕈领主(分治FFT)
传送门 话说分治\(FFT\)是个啥子啊--还有题目里那字好像念(蕈xùn) 首先考虑无解的情况:区间相交或者\(L_n\neq n\) 这两个都可以感性理解一下 所以区间之间只会有包含关系,我们把每 ...
- 并不对劲的bzoj5341:loj2553:uoj400:p4565:[Ctsc2018]暴力写挂
题目大意 有两棵\(n\)(\(n\leq366666\))个节点的树,\(T\)和\(T'\),有边权 \(dep(i)\)表示在\(T\)中\(i\)号点到\(1\)号点的距离,\(dep'(i) ...
- 并不对劲的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* ...
- CTSC2018 & APIO2018 颓废 + 打铁记
CTSC2018 & APIO2018 颓废 + 打铁记 CTSC 5 月 6 日 完美错过报道,到酒店领了房卡放完行李后直接奔向八十中拿胸牌.饭票和资料.试机时是九省联考的题,从来没做过,我 ...
随机推荐
- C. The Smallest String Concatenation-C++sort排序~~
C. The Smallest String Concatenation time limit per test 3 seconds memory limit per test 256 megabyt ...
- 【BZOJ2733】永无乡(线段树,启发式合并)
题意:支持合并,求块内K小数 对于 100%的数据 n≤100000,m≤n,q≤300000 思路:对于每一个块建立一棵动态开点的线段树,暴力(启发式?)合并后二分下就行了 merge用函数的方式写 ...
- CodeIgniter与Zend Acl结合实现轻量级权限控制
CodeIgniter与Zend Acl结合实现轻量级权限控制 Tag :CodeIgniter Zend Acl 权限控制 1. Zend_Acl简介 Zend_Acl 为权限管理提供轻量并灵活的访 ...
- Android 学习路线图(转载自https://blog.csdn.net/lixuce1234/article/details/77947405)
程序设计 一.java (a)基本语法(如继承.异常.引用.泛型等) Java核心技术 卷I(适合入门) 进阶 Effective Java中文版(如何写好的Java代码) Java解惑 (介绍烂Ja ...
- lombok注解
官方文档:@EqualsAndHashCode 转:https://blog.csdn.net/zhanlanmg/article/details/50392266 1. 此注解会生成equals(O ...
- Javascript小数取整方法收集
1.丢弃小数部分,保留整数部分 parseInt(7/2) 2.向上取整,有小数就整数部分加1 Math.ceil(7/2) 3.四舍五入 Math.round(7/2) 4.向下取整 Math.fl ...
- Android中传递对象的三种方法
Android知识.前端.后端以至于产品和设计都有涉猎,想成为全栈工程师的朋友不要错过! Android中,Activity和Fragment之间传递对象,可以通过将对象序列化并存入Bundle或者I ...
- Android 开源框架ViewPageIndicator 和 ViewPager 仿网易新闻clientTab标签
之前用JakeWharton的开源框架ActionBarSherlock和ViewPager实现了对网易新闻clientTab标签的功能,ActionBarSherlock是在3.0下面的机器支持Ac ...
- vi和vim上查找字符串
方法/步骤 1 我们以samba的配置文件为例,搜索一个user的字符串. vim /etc/samba/smb.conf 打开smb.conf 2 命令模式下,输入/user "/&quo ...
- centos 安装python2.7
安装pip sudo yum -y install epel-release sudo yum -y install python-pip 下载解压Python-2.7.3 #wget http:// ...