Codeforces 1553I - Stairs(分治 NTT+容斥)
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+容斥)的更多相关文章
- 51nod 1514 美妙的序列 分治NTT + 容斥
Code: #include<bits/stdc++.h> #define ll long long #define mod 998244353 #define maxn 400000 # ...
- 【题解】[HAOI2018]染色(NTT+容斥/二项式反演)
[题解][HAOI2018]染色(NTT+容斥/二项式反演) 可以直接写出式子: \[ f(x)={m \choose x}n!{(\dfrac 1 {(Sx)!})}^x(m-x)^{n-Sx}\d ...
- Codeforces Round #258 (Div. 2) 容斥+Lucas
题目链接: http://codeforces.com/problemset/problem/451/E E. Devu and Flowers time limit per test4 second ...
- 洛谷 P2634 [国家集训队]聪聪可可-树分治(点分治,容斥版) +读入挂+手动O2优化吸点氧才过。。。-树上路径为3的倍数的路径数量
P2634 [国家集训队]聪聪可可 题目描述 聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃.两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一 ...
- Codeforces.449D.Jzzhu and Numbers(容斥 高维前缀和)
题目链接 \(Description\) 给定\(n\)个正整数\(a_i\).求有多少个子序列\(a_{i_1},a_{i_2},...,a_{i_k}\),满足\(a_{i_1},a_{i_2}, ...
- Jzzhu and Numbers CodeForces - 449D (高维前缀和,容斥)
大意: 给定集合a, 求a的按位与和等于0的非空子集数. 首先由容斥可以得到 $ans = \sum \limits_{0\le x <2^{20}} (-1)^{\alpha} f_x$, 其 ...
- Codeforces 595B. Pasha and Phone 容斥
B. Pasha and Phone time limit per test 1 second memory limit per test 256 megabytes input standard i ...
- BZOJ3771 Triple 【NTT + 容斥】
题目链接 BZOJ3771 题解 做水题放松一下 先构造\(A_i\)为\(x\)指数的生成函数\(A(x)\) 再构造\(2A_i\)为指数的生成函数\(B(x)\) 再构造\(3A_i\)为指数的 ...
- 洛谷 P3806 【模板】点分治1-树分治(点分治,容斥版) 模板题-树上距离为k的点对是否存在
P3806 [模板]点分治1 题目背景 感谢hzwer的点分治互测. 题目描述 给定一棵有n个点的树 询问树上距离为k的点对是否存在. 输入格式 n,m 接下来n-1条边a,b,c描述a到b有一条长度 ...
随机推荐
- .Net Core微信服务商二次进件
最近商城进行微信服务商二次进件的开发,大致有几个点 一,服务商签名 二,服务商证书获取 三,图片上传 四,敏感信息加密 五,查询进件状态 除此之外,就是进件信息的拼装 电商二级商户进件申请单-状态流转 ...
- 半天撸一个简易版mybatis
为什么需要持久层框架? 首先我们先看看使用原生jdbc存在的问题? public static void main(String[] args) { Connection connection = n ...
- 【Java虚拟机4】Java内存模型(硬件层面的并发优化基础知识--缓存一致性问题)
前言 今天学习了Java内存模型第一课的视频,讲了硬件层面的知识,还是和大学时一样,醍醐灌顶.老师讲得太好了. Java内存模型,感觉以前学得比较抽象.很繁杂,抽象. 这次试着系统一点跟着2个老师学习 ...
- JAVA String、StringBuffer、StringBuilder类解读
JAVA String.StringBuffer.StringBuilder类解读 字符串广泛应用 在 Java 编程中,在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作 ...
- Tekton+Argocd实现自动化流水线
目录 什么是tekton 安装tekton 安装Dashboard Tekton提供的CRD 安装argocd 创建argocd 安装客户端 连接argocd server 创建App 集群中查看效果 ...
- SpringMvc 中 FrameworkServlet 覆盖 service 的有点。
@Override protected void service(HttpServletRequest request, HttpServletResponse response) throws Se ...
- 2021.9.28考试总结[NOIP模拟64]
T1 三元组 发现确定\(b,c\)的情况下,\(a\)的值域是连续的.确定\(b\)后\(a+b\)的取值是\([1+b,b+b]\).树状数组维护对每个\(b\)可行的\(c\). 注意取模后取值 ...
- 如何清理history
工作中,需要清理history 清理当前会话历史命令 history -c 清理当前用户所有历史命令 echo > .bash_history #在用户主目录执行此操作
- QT判断文件/目录是否存在
最近在用qt写一个ui,遇到删除sd卡中的文件失败情况,有些时候是存在删除链表里面的文件在sd卡上已经不存在了,导致失败,以为我的链表是定时刷新的,但是文件是实时更新会同步覆盖的.这样就存在可能上一秒 ...
- 检查是否是BST 牛客网 程序员面试金典 C++ java Python
检查是否是BST 牛客网 程序员面试金典 C++ java Python 题目描述 请实现一个函数,检查一棵二叉树是否为二叉查找树. 给定树的根结点指针TreeNode* root,请返回一个boo ...