Codeforces 1175F - The Number of Subpermutations(线段树+单调栈+双针/分治+启发式优化)
由于这场的 G 是道毒瘤题,蒟蒻切不动就只好来把这场的 F 水掉了
看到这样的设问没人想到这道题吗?那我就来发篇线段树+单调栈的做法。
首先显然一个区间 \([l,r]\) 满足条件当且仅当:
- \([l,r]\) 中不存在重复的数值
- \([l,r]\) 中最小值为 \(1\)
- \([l,r]\) 中最大值与最小值的差为 \(r-l\)
题解区中某位大佬说过:“数区间的题无非两种套路,枚举端点和分治”,这里咱们考虑枚举端点。具体来说,咱们枚举右端点 \(r\),那么满足 \([l,r]\) 中不存在重复的数值的 \(l\) 显然组成了一段连续的区间,且这个区间的右端点就是 \(r\),因此可以在枚举右端点的同时 two pointers 找出满足“\([l,r]\) 中不存在重复的数值”的最大的 \(l\),设为 \(l'\) 这样我们左端点只用在 \([l',r]\) 中取值即可,这样第一个条件就解决了。第二个条件其实也异常 simple,我们只用找出上一个 \(1\) 所在的位置 \(p\),如果 \(p<l'\) 那咱们就忽略这个区间,否则显然你区间的左端点必须 \(\le p\),这样咱们区间的左端点的范围就进一步缩小到了 \([l',p]\)。比较棘手的是第三个条件,不过按照那题的套路,第三个条件可以转化为 \(\max\limits_{i=l}^ra_i-(r-l+1)=0\),又因为 \(\forall l\in[l',p]\),区间 \([l,r]\) 中的数互不相同,因此 \(\forall l\in[l',p],\max\limits_{i=l}^ra_i-(r-l+1)\ge 0\),因此我们可以开一棵线段树,线段树上 \(l\) 位置上的值就是 \(\max\limits_{i=l}^ra_i-(r-l+1)\),显然该线段树可以单调栈维护,那么我们只需要求出 \([l',p]\) 最小值即最小值个数即可算出贡献,具体来说如果最小值不为 \(0\) 那么贡献为 \(0\),否则贡献就是最小值的个数。
时间复杂度 \(\mathcal O(n\log n)\)
const int MAXN=3e5;
int n,a[MAXN+5];
struct node{int l,r;pii p;ll lz;} s[MAXN*4+5];
pii operator +(pii lhs,pii rhs){
pii res;res.fi=min(lhs.fi,rhs.fi);
if(res.fi==lhs.fi) res.se+=lhs.se;
if(res.fi==rhs.fi) res.se+=rhs.se;
return res;
}
void pushup(int k){s[k].p=s[k<<1].p+s[k<<1|1].p;}
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;if(l==r) return s[k].p=mp(0,1),void();
int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);pushup(k);
}
void pushdown(int k){
if(s[k].lz){
s[k<<1].p.fi+=s[k].lz;s[k<<1].lz+=s[k].lz;
s[k<<1|1].p.fi+=s[k].lz;s[k<<1|1].lz+=s[k].lz;
s[k].lz=0;
}
}
void modify(int k,int l,int r,int x){
if(l<=s[k].l&&s[k].r<=r){
s[k].p.fi+=x;s[k].lz+=x;return;
} pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid) modify(k<<1,l,r,x);
else if(l>mid) modify(k<<1|1,l,r,x);
else modify(k<<1,l,mid,x),modify(k<<1|1,mid+1,r,x);
pushup(k);
}
pii query(int k,int l,int r){
if(l<=s[k].l&&s[k].r<=r) return s[k].p;
pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid) return query(k<<1,l,r);
else if(l>mid) return query(k<<1|1,l,r);
else return query(k<<1,l,mid)+query(k<<1|1,mid+1,r);
}
int pre[MAXN+5],cnt[MAXN+5];
int main(){
scanf("%d",&n);build(1,1,n);ll res=0;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
stack<int> stk;stk.push(0);a[0]=0x3f3f3f3f;
for(int i=1,j=1;i<=n;i++){
cnt[a[i]]++;while(cnt[a[i]]>=2) cnt[a[j++]]--;
pre[a[i]]=i;modify(1,1,i,-1);modify(1,i,i,a[i]);
while(!stk.empty()&&a[stk.top()]<a[i]){
int x=stk.top();stk.pop();
modify(1,stk.top()+1,x,a[i]-a[x]);
} stk.push(i);if(pre[1]>=j){
pii p=query(1,j,pre[1]);
if(!p.fi) res+=p.se;
}
} printf("%ld\n",res);
return 0;
}
还有一种思路便是分治(既然上面咱们选择了枚举端点,那这边咱们就要选择分治咯)
我们首先考虑怎样判断一个区间是否存在相同元素,按照区间数颜色的套路,我们记 \(p_i\) 表示 \(i\) 前面上一个与 \(a_i\) 相等的 \(a_j\) 的位置,那么区间 \([l,r]\) 不存在重复元素的充要条件是 \(\max\limits_{i=l}^rp_i<l\)。考虑分治,处理左右端点 \([l,r]\) 都在 \([l,r]\) 中的区间时,我们找出区间最大值所在的位置 \(p\),那么显然 \([l,r]\) 中的区间可以像点分治那样分成三类:完全包含于 \([l,p-1]\)、完全包含于 \([p+1,r]\),以及跨过 \(p\),前两类显然可以递归处理。关于第三类,显然区间中最大的数就是 \(a_p\),区间长度也就是 \(a_p\),因此我们枚举所有长度为 \(a_p\)、且跨过位置 \(p\)(这点一定要判断)的区间计算贡献即可,但这样会 T,考虑优化,显然区间左端点必须在 \([l,p]\) 中对吧,右端点必须在 \([p,r]\) 中对吧,那么我们就考虑 \([l,p],[p,r]\) 中长度的较小者,如果 \([l,p]\) 长度较小就枚举左端点 \(L\in[l,p]\),否则枚举右端点 \(R\in[p,r]\)。这样乍一看复杂度没啥变化,不过按照这题的套路,这其实相当于启发式合并的逆过程,即启发式分裂(瞎起名字 ing),因此复杂度是严格单 log 的。
非常神奇,谁能告诉我为什么两个程序跑得一样快……358 ms
const int MAXN=3e5;
const int LOG_N=18;
int n,a[MAXN+5],pre[MAXN+5],st[MAXN+5][LOG_N+2],res=0;
pii st_val[MAXN+5][LOG_N+2];
int query(int l,int r){
int k=31-__builtin_clz(r-l+1);
return max(st[l][k],st[r-(1<<k)+1][k]);
}
int query_ps(int l,int r){
int k=31-__builtin_clz(r-l+1);
return max(st_val[l][k],st_val[r-(1<<k)+1][k]).se;
}
void solve(int l,int r){
if(l>r) return;int ps=query_ps(l,r),len=a[ps];
solve(l,ps-1);solve(ps+1,r);
if(ps-l+1<=r-ps+1){
for(int i=l;i<=ps;i++) if(i+len-1<=r&&i+len-1>=ps&&query(i,i+len-1)<i)
res++;
} else {
for(int i=ps;i<=r;i++) if(i-len+1>=l&&i-len+1<=ps&&query(i-len+1,i)<i-len+1)
res++;
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),st_val[i][0]=mp(a[i],i);
for(int i=1;i<=n;i++) st[i][0]=pre[a[i]],pre[a[i]]=i;
for(int i=1;i<=LOG_N;i++) for(int j=1;j+(1<<i)-1<=n;j++){
st[j][i]=max(st[j][i-1],st[j+(1<<i-1)][i-1]);
st_val[j][i]=max(st_val[j][i-1],st_val[j+(1<<i-1)][i-1]);
} solve(1,n);printf("%d\n",res);
return 0;
}
Codeforces 1175F - The Number of Subpermutations(线段树+单调栈+双针/分治+启发式优化)的更多相关文章
- Codeforces 781E Andryusha and Nervous Barriers 线段树 单调栈
原文链接https://www.cnblogs.com/zhouzhendong/p/CF781E.html 题目传送门 - CF781E 题意 有一个矩形,宽为 w ,高为 h .一开始会有 w 个 ...
- 洛谷P4425 转盘 [HNOI/AHOI2018] 线段树+单调栈
正解:线段树+单调栈 解题报告: 传送门! 1551又是一道灵巧连题意都麻油看懂的题,,,,所以先解释一下题意好了,,,, 给定一个n元环 可以从0时刻开始从任一位置出发 每次可以选择向前走一步或者在 ...
- 线段树+单调栈+前缀和--2019icpc南昌网络赛I
线段树+单调栈+前缀和--2019icpc南昌网络赛I Alice has a magic array. She suggests that the value of a interval is eq ...
- 牛客多校第四场sequence C (线段树+单调栈)
牛客多校第四场sequence C (线段树+单调栈) 传送门:https://ac.nowcoder.com/acm/contest/884/C 题意: 求一个$\max {1 \leq l \le ...
- [Codeforces1132G]Greedy Subsequences——线段树+单调栈
题目链接: Codeforces1132G 题目大意:给定一个序列$a$,定义它的最长贪心严格上升子序列为$b$满足若$a_{i}$在$b$中则$a_{i}$之后第一个比它大的也在$b$中.给出一个数 ...
- BZOJ.4540.[HNOI2016]序列(莫队/前缀和/线段树 单调栈 RMQ)
BZOJ 洛谷 ST表的一二维顺序一定要改过来. 改了就rank1了哈哈哈哈.自带小常数没办法. \(Description\) 给定长为\(n\)的序列\(A_i\).\(q\)次询问,每次给定\( ...
- AtCoder Regular Contest 063 F : Snuke’s Coloring 2 (线段树 + 单调栈)
题意 小 \(\mathrm{C}\) 很喜欢二维染色问题,这天他拿来了一个 \(w × h\) 的二维平面 , 初始时均为白色 . 然后他在上面设置了 \(n\) 个关键点 \((X_i , Y_i ...
- cdqz2017-test10-rehearsal(CDQ分治&可持久化线段树&单调栈)
题意: 给出n个三元组 e[i]=(si,ti,wi) 第i个三元组的价值为 Σ w[j] ,j 满足以下4个条件: 1.j<i 2.tj<ti 3.sj<si 4.不存在j< ...
- 2018.09.22 atcoder Snuke's Coloring 2(线段树+单调栈)
传送门 就是给出一个矩形,上面有一些点,让你找出一个周长最大的矩形,满足没有一个点在矩形中. 这个题很有意思. 考虑到答案一定会穿过中线. 于是我们可以把点分到中线两边. 先想想暴力如何解决. 显然就 ...
随机推荐
- rocketMQ(一)基础环境
一.安装: http://rocketmq.apache.org/dowloading/releases/ https://www.apache.org/dyn/closer.cgi?path=roc ...
- DDD领域驱动设计-案例建模设计-Ⅲ
1. 背景 参考<DDD领域驱动设计-案例需求文档>,本文将构建实体,聚合根详述领域驱动中的建模设计.构建实体,聚合根的一些原则或方法,将在后续文章中说明. 2. 建模设计 2.1. 实体 ...
- 用建造者模式实现一个防SQL注入的ORM框架
本文节选自<设计模式就该这样学> 1 建造者模式的链式写法 以构建一门课程为例,一个完整的课程由PPT课件.回放视频.课堂笔记.课后作业组成,但是这些内容的设置顺序可以随意调整,我们用建造 ...
- 2021.7.29考试总结[NOIP模拟27]
T1 牛半仙的妹子图 做法挺多的,可以最小生成树或者最短路,复杂度O(cq),c是颜色数. 我考场上想到了原来做过的一道题影子,就用了并查集,把边权排序后一个个插入,记录权值的前缀和,复杂度mlogm ...
- 单片机STM32的5个时钟源知识
众所周知STM32有5个时钟源HSI.HSE.LSI.LSE.PLL,其实他只有四个,因为从上图中可以看到PLL都是由HSI或HSE提供的. 其中,高速时钟(HSE和HSI)提供给芯片主体的主时钟.低 ...
- hdu 2200 Eddy's AC难题(简单数学。。)
题意: N个人,每个人AC的题数都不一样. Eddy想从中选出一部分人(或者全部)分成两组.必须满足第一组中的最小AC数大于第二组中的最大AC数. 问共有多少种不同的选择方案. 思路: 简单数学.. ...
- hdu 5093 Battle ships(二分图最大匹配)
题意: M*N的矩阵,每个格子上是三个之一:*.o.#. (1 <= m, n <= 50) *:海洋,战船可以停在上面. o:浮冰,战船 ...
- poj 3417 Network (LCA,路径上有值)
题意: N个点,构成一棵树.给出这棵树的结构. M条边,(a1,b1)...(am,bm),代表给树的这些点对连上边.这样就形成了有很多环的一个新"树". 现在要求你在原树中断一条 ...
- 从零开始,无DNS vcenter 6.7 vmotion热迁移,存储集群部署文档。
1,环境准备 准备:Vmware workstation环境 IP地址段规划 ESXI主机IP地址段 192.168.197.4-192.168.197.10 Vcenter Server集群IP地址 ...
- 记录自己的踩坑第一天 | CSS:vertical-align 属性
前言 最近老师让大家单独写前后端分离项目,真是大家卷完后端,一起去卷前端了.(我以前都是主要负责后端,处于只大致看的懂的级别,说多了都是泪啊). 真是处于一边学一边写的状态,基本就是每天早上看上两~三 ...