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

感觉这个 *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(线段树)的更多相关文章

  1. codeforces 652D . Nested Segments 线段树

    题目链接 我们将线段按照右端点从小到大排序, 如果相同, 那么按照左端点从大到小排序. 然后对每一个l, 查询之前有多少个l比他大, 答案就是多少.因为之前的r都是比自己的r小的, 如果l还比自己大的 ...

  2. Buses and People CodeForces 160E 三维偏序+线段树

    Buses and People CodeForces 160E 三维偏序+线段树 题意 给定 N 个三元组 (a,b,c),现有 M 个询问,每个询问给定一个三元组 (a',b',c'),求满足 a ...

  3. CodeForces 877E DFS序+线段树

    CodeForces 877E DFS序+线段树 题意 就是树上有n个点,然后每个点都有一盏灯,给出初始的状态,1表示亮,0表示不亮,然后有两种操作,第一种是get x,表示你需要输出x的子树和x本身 ...

  4. [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路)

    [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路) 题面 有n个空心物品,每个物品有外部体积\(out_i\)和内部体积\(in_i\),如果\(in_i& ...

  5. [Codeforces 1199D]Welfare State(线段树)

    [Codeforces 1199D]Welfare State(线段树) 题面 给出一个长度为n的序列,有q次操作,操作有2种 1.单点修改,把\(a_x\)修改成y 2.区间修改,把序列中值< ...

  6. [Codeforces 316E3]Summer Homework(线段树+斐波那契数列)

    [Codeforces 316E3]Summer Homework(线段树+斐波那契数列) 顺便安利一下这个博客,给了我很大启发(https://gaisaiyuno.github.io/) 题面 有 ...

  7. 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 ...

  8. 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 ...

  9. Codeforces Round #337 (Div. 2) D. Vika and Segments (线段树+扫描线+离散化)

    题目链接:http://codeforces.com/contest/610/problem/D 就是给你宽度为1的n个线段,然你求总共有多少单位的长度. 相当于用线段树求面积并,只不过宽为1,注意y ...

随机推荐

  1. 从源码层面深度剖析Redisson实现分布式锁的原理(全程干货,注意收藏)

    Redis实现分布式锁的原理 前面讲了Redis在实际业务场景中的应用,那么下面再来了解一下Redisson功能性场景的应用,也就是大家经常使用的分布式锁的实现场景. 引入redisson依赖 < ...

  2. 【UE4 C++】UGameplayStatics 源代码

    // Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" # ...

  3. 【UE4 设计模式】工厂方法模式 Factory Method Pattern 及自定义创建资源

    概述 描述 又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式,或者多态工厂(Polymorphic Factory)模式 工厂父类负责定义创建产品对象的公共接口,而工厂子类 ...

  4. Java:创建对象小记

    Java:创建对象小记 对 Java 中的创建对象的内容,做一个微不足道的小小小小记 创建对象的方式概述 使用 new 关键字:Person person = new Person(); 反射创建:使 ...

  5. BUAA软件工程个人博客作业

    软件工程个人博客作业 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 个人博客作业 我在这个课程的目标 团队完成好的软件,并对自己作出规划 这个作 ...

  6. Ubuntu mysql安装与使用

    Ubuntu 下安装 mysql 运行下面的shell代码 #安装mysql sudo apt-get -y install mysql-server sudo apt-get -y install ...

  7. 三层组网AP上线外接DHCP

    一.实验目的 在3-1的基础上增加DHCP的配置方法 二.实验仪器设备及软件 仪器设备:一台AC,四台AP,一台路由充当DHCP服务器 软件:ENSP 三.实验原理   四. 实验内容与步骤 1.三层 ...

  8. zabbix 报警发送企业威信

    1.组册企业微信,创建应用 2.下载脚本文件: https://raw.githubusercontent.com/OneOaaS/weixin-alert/master/weixin_linux_a ...

  9. 【解决方案】Pyinstaller打包exe策略(简单实用)

    场景说明 在业务场景中, 经常需要Python开发一些小程序/脚本/GUI界面,进行简单的项目测试或未安装Python 的小伙伴们使用. 使用Pyinstaller将Python脚本或者项目打包,生成 ...

  10. js运算符 及 运算符优先级

    「运算符」是用于实现赋值.比较和执行算数运算等功能的符号.常用运算符分类如下符号 算数运算符 递增和递减运算符 比较运算符 逻辑运算符 赋值运算符 算数运算符 运算符 描述 案例 + 加 10+20= ...