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 ...
随机推荐
- SingnalR 从开发到生产部署闭坑指南
前天倒腾了一份[SignalR在react/go技术栈的实践], 步骤和思路大部分是外围框架的应用, 今天趁热打铁, 给一个我总结的SignalR避坑指南. 1.SignalR 默认协商 不管是.NE ...
- JAVA复习总体大纲
1 java基础. [1].变量--- 数据类型 变量名=值; 数据类型: 1.基本数据类型. byte[1字节] short[2字节] int[4字节] long[8字节] float[4字节] d ...
- 关于takin-data,你想知道的都在这里(一)启动命令篇
通过docker部署体验takin的小伙伴都应该知道,在安装部署手册中有提到:在启动surge-deploy任务前,需要将启动命令中的ip参数替换为docker容器所在宿主机的ip,很多小伙伴都在这里 ...
- Mybatis 动态Sql练习
建表 CREATE TABLE `student` ( `s_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT ...
- Alpha Scrum Meeting汇总
第一次Alpha Scrum Meeting 第二次Alpha Scrum Meeting 第三次Alpha Scrum Meeting 第四次Alpha Scrum Meeting 第五次Alpha ...
- 写一段java程序来执行linux命令
摘要 在日常开发中,程序员需要经常查询服务器日志来排查问题和调试程序.如果是本地调试还好,但项目一旦发布到服务器上,每次查日志就很麻烦,而且日志量巨大,有时我们无法找到我们需要的信息.经常需要借助第三 ...
- hdu 2201 熊猫阿波的故事(简单概率。。)
题意: 阿波上了飞机,飞机上有座位1,2,....,N.第i个乘客本应坐在第i个座位上. 可是阿波随便找了个座位就坐了下来,接下来大家也都随便找了个座位坐了下来. 问:第i个乘客坐到原座位的概率是多少 ...
- S 锁与 X 锁的爱恨情仇《死磕MySQL系列 四》
系列文章 一.原来一条select语句在MySQL是这样执行的<死磕MySQL系列 一> 二.一生挚友redo log.binlog<死磕MySQL系列 二> 三.MySQL强 ...
- springboot利用mock进行junit单元测试,测试controller
1 spring-boot-starter-test内置mockito,添加pom依赖 <dependency> <groupId>org.springframework.b ...
- Apache Shiro 反序列化漏洞分析
Shiro550 环境搭建 参考:https://www.cnblogs.com/twosmi1e/p/14279403.html 使用Docker vulhub中的环境 docker cp 将容器内 ...