Codeforces 题面传送门 & 洛谷题面传送门

u1s1 感觉这道题放到 D1+D2 里作为 5250 分的 I 有点偏简单了吧

首先一件非常显然的事情是,如果我们已知了排列对应的阶梯序列,那么排列中每个极长的连续阶梯段就已经确定了,具体来说,由于显然极大的连续段之间不能相交,因此假设 \(a\) 为 \(p\) 的阶梯序列,对于 \(a\) 数组中每个值相同的极大连续段 \([l,r]\),显然我们只能每 \(a_l\) 个元素将其划分成 \([l,l+a_l-1],[l+a_l,l+2a_l-1],\cdots\) 这样 \(\dfrac{r-l+1}{a_l}\) 个连续段,当然如果 \((r-l+1)\bmod a_l\ne 0\) 那答案肯定就是 \(0\) 了。因此这样我们考虑将所有极大连续段的长度排成一个序列 \(b_1,b_2,\cdots,b_m\),那么我们如果将每个极大段中最小的数拎出来离散化,肯定会形成一个 \(1\sim m\) 的排列,那么我们的任务就是给每个数安排上一个 \(1\sim m\) 的编号,使得这些编号组成一个 \(1\sim m\)​ 的排列,并给每个连续段钦定一个顺序(从大到小 or 从小到大),值得注意的是,如果 \(b_i=1\),那么从小到大和从大到小是相同的,只有一种钦定方式,记 \(c_i\) 为第 \(i\) 个连续段的编号,那么一个钦定方式合法当且仅当不存在以下两种情形:

  • \(\exists i\in[1,m-1],s.t.c_{i+1}=c_i+1\) 且第 \(i\) 个连续段和第 \(i+1\) 个连续段都是从小到大
  • \(\exists i\in[1,m-1],s.t.c_{i+1}=c_i-1\)​ 且第 \(i\)​ 个连续段和第 \(i+1\)​ 个连续段都是从大到小

直接做肯定不行,因此考虑容斥。我们考虑将这些连续段划分成 \(k\)​ 段,并且每一段我们钦定这一段中的数必须构成一个大的阶梯序列,那么对于一个 \(k\)​,其容斥系数自然就是 \((-1)^{m-k}\)​。那么怎么求对于一个 \(k\)​ 的答案呢?考虑 \(dp\)​,\(dp_{i,j}\)​ 表示前 \(i\)​ 个数划分成 \(j\)​ 段的方案数,转移就枚举上一段的结束位置 \(l\)​,如果 \(i-l=1\)​ 且 \(b_i=1\)​ 那么 \(dp_{i,j}\leftarrow dp_{l,j-1}\)​,否则 \(dp_{i,j}\leftarrow dp_{l,j-1}·2\)​,一目了然。最终答案 \(ans=\sum\limits_{i=1}^m(-1)^{m-i}i!·dp_{m,i}\)

这样直接做是三方的,前缀和优化一下可以做到平方,但还是不够,考虑继续优化。首先注意到最后统计答案涉及阶乘,而这个东西不像 \((-1)^i\)​​​​ 能够搞在状态里面,因此第二维“划分成多少段”不能省。那么怎么办呢?就分治 NTT 呗。注意到一段连续的连续段如果贡献为 \(2\)​​​,那么它不论怎么拼贡献还是 \(2\)​​​,因此当我们分治一段区间 \([l,r]\)​​​,我们设 \(f_{k,x,y}\)​​​ 表示有多少种划分 \([l,r]\)​​​ 中连续段的方法,满足最左边一个连续段的贡献为 \(x\)​,最右边的一个连续段的贡献为 \(y\)​,其中 \(0\)​ 表示 \(1\)​,\(1\)​ 表示 \(2\)​​​,合并左右区间时就分 \(mid,mid+1\)​ 被划分在同一段和不划分在同一段中两种情况即可。最后就按照上面的式子计算答案即可。

时间复杂度 \(n\log^2n\),不过由于自带 \(48\) 的常数所以喜提最劣解/qd

个别细节见代码吧:

const int MAXN=1e5;
const int MAXP=1<<18;
const int pr=3;
const int ipr=332748118;
const int MOD=998244353;
const int INV2=499122177;
int qpow(int x,int e){
int ret=1;
for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
return ret;
}
int rev[MAXP+5];
void NTT(vector<int> &a,int len,int type){
int lg=31-__builtin_clz(len);
for(int i=0;i<len;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<lg-1);
for(int i=0;i<len;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int i=2;i<=len;i<<=1){
int W=qpow((type<0)?ipr:pr,(MOD-1)/i);
for(int j=0;j<len;j+=i){
for(int k=0,w=1;k<(i>>1);k++,w=1ll*w*W%MOD){
int X=a[j+k],Y=1ll*w*a[(i>>1)+j+k]%MOD;
a[j+k]=(X+Y)%MOD;a[(i>>1)+j+k]=(X-Y+MOD)%MOD;
}
}
} if(!~type){
int ivn=qpow(len,MOD-2);
for(int i=0;i<len;i++) a[i]=1ll*a[i]*ivn%MOD;
}
}
vector<int> conv(vector<int> a,vector<int> b){
int LEN=1;while(LEN<a.size()+b.size()) LEN<<=1;
a.resize(LEN,0);b.resize(LEN,0);NTT(a,LEN,1);NTT(b,LEN,1);
for(int i=0;i<LEN;i++) a[i]=1ll*a[i]*b[i]%MOD;NTT(a,LEN,-1);
return a;
}
int n,m,a[MAXN+5],b[MAXN+5],fac[MAXN+5];
void init_fac(int n){for(int i=(fac[0]=1);i<=n;i++) fac[i]=1ll*fac[i-1]*i%MOD;}
struct dat{
vector<int> v[2][2];
void resize(int x){
for(int i=0;i<2;i++) for(int j=0;j<2;j++)
v[i][j].resize(x+1);
}
vector<int>* operator [](int x){return v[x];}
};
dat solve(int l,int r){
if(r-l+1<=3){
dat ret;ret.resize(r-l+1);
if(r-l+1==2){
ret[b[l]][b[r]][2]=(b[l]+1)*(b[r]+1);
ret[1][1][1]=2;
} else {
ret[b[l]][b[r]][3]=(b[l]+1)*(b[r]+1)*(b[l+1]+1);
ret[b[l]][1][2]=(b[l]+1<<1);ret[1][b[r]][2]+=(b[r]+1<<1);
ret[1][1][1]=2;
} return ret;
} int mid=l+r>>1;
dat L=solve(l,mid),R=solve(mid+1,r);
dat ret;ret.resize(r-l+1);
for(int i=0;i<2;i++) for(int j=0;j<2;j++){
for(int x=0;x<2;x++) for(int y=0;y<2;y++){
vector<int> tmp=conv(L[i][x],R[y][j]);
// printf("%d %d %d %d\n",i,x,y,j);
for(int t=1;t<=r-l+1;t++) ret[i][j][t]=(ret[i][j][t]+tmp[t])%MOD;
for(int t=1;t<=r-l;t++){
if(x+y==2) ret[i][j][t]=(ret[i][j][t]+1ll*tmp[t+1]*INV2)%MOD;
else if(x+y==1) ret[i][j][t]=(ret[i][j][t]+tmp[t+1])%MOD;
else ret[i][j][t]=(ret[i][j][t]+2ll*tmp[t+1])%MOD;
}
}
} return ret;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int l=1,r;l<=n;l=r){
r=l;while(a[l]==a[r]) ++r;
if((r-l)%a[l]) return puts("0"),0;
for(int j=1;j<=(r-l)/a[l];j++) b[++m]=(a[l]>1);
} init_fac(m);
if(m==1) return printf("%d\n",(b[1])?2:1),0;
dat res=solve(1,m);int ans=0;
for(int i=1;i<=m;i++){
int sum=0;
for(int j=0;j<2;j++) for(int k=0;k<2;k++) sum=(sum+res[j][k][i])%MOD;
if((m-i)&1) ans=(ans-1ll*sum*fac[i]%MOD+MOD)%MOD;
else ans=(ans+1ll*sum*fac[i])%MOD;
} printf("%d\n",ans);
return 0;
}

Codeforces 1553I - Stairs(分治 NTT+容斥)的更多相关文章

  1. 51nod 1514 美妙的序列 分治NTT + 容斥

    Code: #include<bits/stdc++.h> #define ll long long #define mod 998244353 #define maxn 400000 # ...

  2. 【题解】[HAOI2018]染色(NTT+容斥/二项式反演)

    [题解][HAOI2018]染色(NTT+容斥/二项式反演) 可以直接写出式子: \[ f(x)={m \choose x}n!{(\dfrac 1 {(Sx)!})}^x(m-x)^{n-Sx}\d ...

  3. Codeforces Round #258 (Div. 2) 容斥+Lucas

    题目链接: http://codeforces.com/problemset/problem/451/E E. Devu and Flowers time limit per test4 second ...

  4. 洛谷 P2634 [国家集训队]聪聪可可-树分治(点分治,容斥版) +读入挂+手动O2优化吸点氧才过。。。-树上路径为3的倍数的路径数量

    P2634 [国家集训队]聪聪可可 题目描述 聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃.两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一 ...

  5. Codeforces.449D.Jzzhu and Numbers(容斥 高维前缀和)

    题目链接 \(Description\) 给定\(n\)个正整数\(a_i\).求有多少个子序列\(a_{i_1},a_{i_2},...,a_{i_k}\),满足\(a_{i_1},a_{i_2}, ...

  6. Jzzhu and Numbers CodeForces - 449D (高维前缀和,容斥)

    大意: 给定集合a, 求a的按位与和等于0的非空子集数. 首先由容斥可以得到 $ans = \sum \limits_{0\le x <2^{20}} (-1)^{\alpha} f_x$, 其 ...

  7. Codeforces 595B. Pasha and Phone 容斥

    B. Pasha and Phone time limit per test 1 second memory limit per test 256 megabytes input standard i ...

  8. BZOJ3771 Triple 【NTT + 容斥】

    题目链接 BZOJ3771 题解 做水题放松一下 先构造\(A_i\)为\(x\)指数的生成函数\(A(x)\) 再构造\(2A_i\)为指数的生成函数\(B(x)\) 再构造\(3A_i\)为指数的 ...

  9. 洛谷 P3806 【模板】点分治1-树分治(点分治,容斥版) 模板题-树上距离为k的点对是否存在

    P3806 [模板]点分治1 题目背景 感谢hzwer的点分治互测. 题目描述 给定一棵有n个点的树 询问树上距离为k的点对是否存在. 输入格式 n,m 接下来n-1条边a,b,c描述a到b有一条长度 ...

随机推荐

  1. DataX的安装及使用

    DataX的安装及使用 目录 DataX的安装及使用 DataX的安装 DataX的使用 stream2stream 编写配置文件stream2stream.json 执行同步任务 执行结果 mysq ...

  2. relativeLayout相对布局的嵌套在py中的引用

    from kivy.app import App from kivy.uix.button import Button from kivy.uix.relativelayout import Rela ...

  3. Redis:学习笔记-01

    Redis:学习笔记-01 该部分内容,参考了 bilibili 上讲解 Redis 中,观看数最多的课程 Redis最新超详细版教程通俗易懂,来自 UP主 遇见狂神说 1. Redis入门 2.1 ...

  4. mongodb的聚合操作

    在mongodb中有时候我们需要对数据进行分析操作,比如一些统计操作,这个时候简单的查询操作(find)就搞不定这些需求,因此就需要使用  聚合框架(aggregation) 来完成.在mongodb ...

  5. BOOST内存管理-intrusive_ptr

    参考链接https://blog.csdn.net/harbinzju/article/details/6754646 intrusive_ptr 是shared_ptr的插入式版本.与shared_ ...

  6. TVS管相关知识

    在设计中,使用到了TVS管,在之前的设计中没有特别关注TVS管.今天查了一些资料,算是简单的有个了解. TVS管是一种保护器件.它的英文全称为 transient voltage suppressor ...

  7. [转]DDR相关的一些基础知识

    ODT ( On-DieTermination ,片内终结)ODT 也是 DDR2 相对于 DDR1 的关键技术突破,所谓的终结(端接),就是让信号被电路的终端吸 收掉,而不会在电路上形成反射, 造成 ...

  8. kail入侵xp实例

    Kali的IP地址是192.168.0.112 Windows XP的IP地址是192.168.0.108 本文演示怎么使用Metasploit入侵windows xp sp3. 启动msfconso ...

  9. 西邮Linux兴趣小组第一次技术分享会

    2016年10月30日晚,西邮Linux兴趣小组技术分享会在西安邮电大学长安校区东区逸夫教学楼FF305室成功举办.200多名来自全校不同专业的15,16级同学参加了此次分享会. 分享会于20:00正 ...

  10. 流媒体技术的应用,如何搭建一个SimpleNVR流媒体服务系统

    Onvif/RTSP流媒体服务 SimpleNVR Onvif/RTSP流媒体服务是一款软硬一体音视频流媒体服务软件.它是在5G.AI.云计算.大数据.物联网等网络技术大规模商用后,用户要求视频随时随 ...