Codeforces 193D - Two Segments(线段树)
感觉这个 *2900 并不难啊,为什么我没想出来呢 awa
顺便膜拜 ycx 一眼秒掉此题 %%%
首先碰到这类题有两种思路,一是枚举两个区间,显然这个思路是不可行的,因为这个思路有四个自由变量 \(l_1,r_1,l_2,r_2\),并且还会出现重复计算的情况,非常棘手。二是注意到 \([l_1,r_1]\cup[l_2,r_2]\) 是一段连续的区间,故考虑枚举这段区间值域的左右端点 \([l,r]\),我们只需统计有多少个区间 \([l,r]\) 满足值在 \([l,r]\) 中的数组成连续段的个数 \(\le 2\) 即可。
我们考虑记录 \(f(l,r)\) 表示值在 \([l,r]\) 中的数组成连续段的个数,\(S(l,r)\) 表示 \(a_p\in[l,r]\) 的 \(p\) 组成的集合。考虑动态枚举 \(r\),建一棵线段树,下标为 \(l\) 的位置实时维护 \([l,r]\) 的个数。考虑右端点从 \(r\) 移到 \(r-1\) 会对 \(f(l,r)\) 的值产生什么影响,假设 \(a_p=r\),首先显然 \(\forall l\le r\),\(p\in S(l,r)\),故我们先假设 \(p\) 单独一段,即令 \([1,r]\) 的值加 \(1\)。其次如果 \(a_{p-1}<r\),那么对于 \(\forall l\le a_{p-1}\),\(p,p-1\) 必定都属于 \(S(l,r)\),因此我们可以将它们合并起来,\(f(l,r)\) 的值减 \(1\),\(a_{p+1}\) 也同理。以上操作都可以通过线段树区间加在 \(\mathcal O(\log n)\) 的时间内搞定。
最后考虑怎样统计答案,显然对于固定的右端点 \(r\),符合条件的 \(l\) 个数即为满足 \(l\in[1,r],f(l,r)\le 2\) 的 \(l\) 个数。直接统计是比较麻烦的,不过这里有一个我见过 N 次的套路,显然 \(\forall l\le r,f(l,r)>0\),因此区间 \([1,r]\) 中最小值 \(\ge 1\),次小值 \(\ge 2\),故我们只需维护最小值、最小值个数、次小值、次小值个数,查询时访问对应区间,如果最小值 \(\le 2\) 答案加上最小值个数,如果次小值 \(\le 2\) 答案加上次小值个数。节点合并就将左右儿子的最小值、次小值放到一个长度为 \(4\) 的数组里排个序乱搞搞即可。
时间复杂度 \(\mathcal O(n\log n)\)。
const int MAXN=3e5;
const int INF=0x3f3f3f3f;
int n,p[MAXN+5],pos[MAXN+5];
struct node{int l,r,lz;pii fst,snd;} s[MAXN*4+5];
void pushup(int k){
static pii p[4];
p[0]=s[k<<1].fst;p[1]=s[k<<1].snd;
p[2]=s[k<<1|1].fst;p[3]=s[k<<1|1].snd;
sort(p,p+4);s[k].fst=mp(p[0].fi,0);int cur=0;
while(p[cur].fi==p[0].fi) s[k].fst.se+=p[cur].se,cur++;
s[k].snd=mp(p[cur].fi,0);
while(p[cur].fi==s[k].snd.fi) s[k].snd.se+=p[cur].se,cur++;
}
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;if(l==r){s[k].fst=mp(0,1);s[k].snd=mp(INF,0);return;}
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].fst.fi+=s[k].lz;s[k<<1].snd.fi+=s[k].lz;s[k<<1].lz+=s[k].lz;
s[k<<1|1].fst.fi+=s[k].lz;s[k<<1|1].snd.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].fst.fi+=x;s[k].snd.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);
}
int query(int k,int l,int r){
if(l<=s[k].l&&s[k].r<=r){
int ret=0;
if(s[k].fst.fi<=2) ret+=s[k].fst.se;
if(s[k].snd.fi<=2) ret+=s[k].snd.se;
return ret;
} 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 main(){
scanf("%d",&n);ll ans=0;build(1,1,n);
for(int i=1;i<=n;i++) scanf("%d",&p[i]),pos[p[i]]=i;
for(int i=1;i<=n;i++){
modify(1,1,i,1);
if(pos[i]!=1&&p[pos[i]-1]<i) modify(1,1,p[pos[i]-1],-1);
if(pos[i]!=n&&p[pos[i]+1]<i) modify(1,1,p[pos[i]+1],-1);
if(i!=1) ans+=query(1,1,i-1);
} printf("%lld\n",ans);
return 0;
}
Codeforces 193D - Two Segments(线段树)的更多相关文章
- codeforces 652D . Nested Segments 线段树
题目链接 我们将线段按照右端点从小到大排序, 如果相同, 那么按照左端点从大到小排序. 然后对每一个l, 查询之前有多少个l比他大, 答案就是多少.因为之前的r都是比自己的r小的, 如果l还比自己大的 ...
- Buses and People CodeForces 160E 三维偏序+线段树
Buses and People CodeForces 160E 三维偏序+线段树 题意 给定 N 个三元组 (a,b,c),现有 M 个询问,每个询问给定一个三元组 (a',b',c'),求满足 a ...
- CodeForces 877E DFS序+线段树
CodeForces 877E DFS序+线段树 题意 就是树上有n个点,然后每个点都有一盏灯,给出初始的状态,1表示亮,0表示不亮,然后有两种操作,第一种是get x,表示你需要输出x的子树和x本身 ...
- [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路)
[Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路) 题面 有n个空心物品,每个物品有外部体积\(out_i\)和内部体积\(in_i\),如果\(in_i& ...
- [Codeforces 1199D]Welfare State(线段树)
[Codeforces 1199D]Welfare State(线段树) 题面 给出一个长度为n的序列,有q次操作,操作有2种 1.单点修改,把\(a_x\)修改成y 2.区间修改,把序列中值< ...
- [Codeforces 316E3]Summer Homework(线段树+斐波那契数列)
[Codeforces 316E3]Summer Homework(线段树+斐波那契数列) 顺便安利一下这个博客,给了我很大启发(https://gaisaiyuno.github.io/) 题面 有 ...
- Codeforces Round #337 (Div. 2) D. Vika and Segments 线段树扫描线
D. Vika and Segments 题目连接: http://www.codeforces.com/contest/610/problem/D Description Vika has an i ...
- Codeforces Round #337 (Div. 2) D. Vika and Segments 线段树 矩阵面积并
D. Vika and Segments Vika has an infinite sheet of squared paper. Initially all squares are whit ...
- Codeforces Round #337 (Div. 2) D. Vika and Segments (线段树+扫描线+离散化)
题目链接:http://codeforces.com/contest/610/problem/D 就是给你宽度为1的n个线段,然你求总共有多少单位的长度. 相当于用线段树求面积并,只不过宽为1,注意y ...
随机推荐
- 81. 搜索旋转排序数组 II
题目 已知存在一个按非降序排列的整数数组 nums ,数组中的值不必互不相同. 在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋 ...
- Java/JDK/J2SE
Java8与JDK1.8与JDK8与J2SE8与J2SE1.8的区别是什么? Java是面向对象的编程语言,在我们开发Java应用的程序员的专业术语里,Java这个单词其实指的是Java开发工具,也就 ...
- Java:volatile笔记
Java:volatile笔记 本笔记是根据bilibili上 尚硅谷 的课程 Java大厂面试题第二季 而做的笔记 1. volatile 和 JMM 内存模型的可见性 JUC 下的三个包 java ...
- UltraSoft - Beta - Scrum Meeting 7
Date: May 23rd, 2020. Scrum 情况汇报 进度情况 组员 负责 今日进度 q2l PM.后端 暂无 Liuzh 前端 编写忘记密码界面 Kkkk 前端 暂无 王fuji 前端 ...
- Pandas核心用法
目录 Numpy和Pandas Numpy科学计算 Pandas数据分析 安装jupyter notebook Numpy语法 创建和基本使用 切片索引 布尔索引 对位运算 矩阵的乘除 其他方法 Pa ...
- 21.6.23 test
省选 模拟赛 今天考的是一套题目背景和描述会被[数据删除]的模拟赛. 犯了几个傻逼错. \(T1\) 把两种情况的概率看反了,写的暴力.\(35->5\) pts. \(T2\) 以为想到了正解 ...
- cesium制作自己的骑行轨迹
制作自己的骑行轨迹 马上国庆节了,计划骑车回家,突然想到把所有的骑行线路汇总一下,无奈码表和APP不支持这样的操作,出于职业病,在此操作一下. 我用的是黑鸟码表,可以导出fit运动轨迹,但是fit还需 ...
- git与pycharm的使用详解(git+gitlab+pycham)
前言 当自动化框架搭建出来后,需要多个人来使用框架,写自动化用例. 在这个阶段,我们不可能将写好的代码打包发给其他人,这样很麻烦,多人协作一点也不灵活. 这时候,就提现出了git的价值 一.下载安装 ...
- kail入侵xp实例
Kali的IP地址是192.168.0.112 Windows XP的IP地址是192.168.0.108 本文演示怎么使用Metasploit入侵windows xp sp3. 启动msfconso ...
- SpringBoot2.x异步任务EnableAsync
1.springboot启动类里面使用@EnableAsync注解开启异步功能 @EnableAsync public class Demo001Application { public static ...