2018宁夏邀请赛 L Continuous Intervals(单调栈+线段树)
2018宁夏邀请赛 L Continuous Intervals(单调栈+线段树)
传送门:https://nanti.jisuanke.com/t/41296
题意:
给一个数列A
问在数列A中有多少个区间满足,区间内数字排序后是一段连续的连续的区间
即排序后区间内的任意元素满足 \(a_{i+1}-a_i<=1\)
题解:
根据题意可知
满足题意的区间应当满足以下性质:
\(max-min==num-1\)
即当前区间最大的数字减去当前区间最小的数字等于区间数字的种类数
我们枚举区间的右端点\(R_i\),那么问题就转换为了有多少个\(L_j,j<=i\)
在区间\(L_j,R_i\)中,
\(Max-Min-num==-1\)
我们需要快速的得到所有满足上式\(L_j\)的个数
也就是 我们需要快速的得到区间 \([1,R_i]\)内满足上式的个数
我们可以用线段树来维护\(Max-Min-num\)这个值
查询就查询有多少个L对应这个值,如果\(Max-Min-num==-1\)
那么对应的值的个数可以对答案产生贡献
线段树维护\(Max-Min-num\)这个值时
我们可以用两个单调栈分别维护Max和Min的值,用map来维护区间数字出现的位置
对于每一次\(Max\)值的更新来说,我们需要减去当前的最大值a[i]-要删去的值的最大值staMax[tp]
对于每一次\(Min\)值的更新来说,我们需要减去要删去的值的最小值staMin[tp]-当前的最小值a[i]
对于每一次出现的数字,他的种类数可能会改变,我们需要在他上一次出现的位置到这个位置这个区间上-1
在查询的时候,我们只需要得到对应的\(Max-Min- num\)最小值为-1的个数即可
代码:
/**
* ┏┓ ┏┓
* ┏┛┗━━━━━━━┛┗━━━┓
* ┃ ┃
* ┃ ━ ┃
* ┃ > < ┃
* ┃ ┃
* ┃... ⌒ ... ┃
* ┃ ┃
* ┗━┓ ┏━┛
* ┃ ┃ Code is far away from bug with the animal protecting
* ┃ ┃ 神兽保佑,代码无bug
* ┃ ┃
* ┃ ┃
* ┃ ┃
* ┃ ┃
* ┃ ┗━━━┓
* ┃ ┣┓
* ┃ ┏┛
* ┗┓┓┏━┳┓┏┛
* ┃┫┫ ┃┫┫
* ┗┻┛ ┗┻┛
*/
// warm heart, wagging tail,and a smile just for you!
//
// _ooOoo_
// o8888888o
// 88" . "88
// (| -_- |)
// O\ = /O
// ____/`---'\____
// .' \| |// `.
// / \||| : |||// \
// / _||||| -:- |||||- \
// | | \ - /// | |
// | \_| ''\---/'' | |
// \ .-\__ `-` ___/-. /
// ___`. .' /--.--\ `. . __
// ."" '< `.___\_<|>_/___.' >'"".
// | | : `- \`.;`\ _ /`;.`/ - ` : | |
// \ \ `-. \_ __\ /__ _/ .-` / /
// ======`-.____`-.___\_____/___.-`____.-'======
// `=---='
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// 佛祖保佑 永无BUG
#include <set>
#include <map>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
typedef unsigned long long uLL;
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define bug printf("*********\n")
#define FIN freopen("input.txt","r",stdin);
#define FON freopen("output.txt","w+",stdout);
#define IO ios::sync_with_stdio(false),cin.tie(0)
#define debug1(x) cout<<"["<<#x<<" "<<(x)<<"]\n"
#define debug2(x,y) cout<<"["<<#x<<" "<<(x)<<" "<<#y<<" "<<(y)<<"]\n"
#define debug3(x,y,z) cout<<"["<<#x<<" "<<(x)<<" "<<#y<<" "<<(y)<<" "<<#z<<" "<<z<<"]\n"
const int maxn = 1e5 + 5;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const double Pi = acos(-1);
LL gcd(LL a, LL b) {
return b ? gcd(b, a % b) : a;
}
LL lcm(LL a, LL b) {
return a / gcd(a, b) * b;
}
double dpow(double a, LL b) {
double ans = 1.0;
while(b) {
if(b % 2)ans = ans * a;
a = a * a;
b /= 2;
} return ans;
}
LL quick_pow(LL x, LL y) {
LL ans = 1;
while(y) {
if(y & 1) {
ans = ans * x % mod;
} x = x * x % mod;
y >>= 1;
} return ans;
}
/*
给你长为 1e5的数组,
要你求其中有多少个区间使得这个区间中的值域是个连续的区间,
即排序后两两个值的差小于等于1。
*/
int Min[maxn << 2];
int num[maxn << 2];
int lazy[maxn << 2];
void push_up(int rt) {
Min[rt] = min(Min[ls], Min[rs]);
if(Min[ls] == Min[rs]) {
num[rt] = num[ls] + num[rs];
} else if(Min[rt] == Min[ls]) {
num[rt] = num[ls];
} else {
num[rt] = num[rs];
}
}
void build(int l, int r, int rt) {
Min[rt] = 0;
lazy[rt] = 0;
num[rt] = r - l + 1;
if(l == r) {
return;
}
int mid = (l + r) >> 1;
build(lson);
build(rson);
// push_up(rt);
}
void push_down(int rt) {
if(lazy[rt]) {
lazy[ls] += lazy[rt];
lazy[rs] += lazy[rt];
Min[ls] += lazy[rt];
Min[rs] += lazy[rt];
lazy[rt] = 0;
}
}
void update(int L, int R, int val, int l, int r, int rt) {
if(L <= l && r <= R) {
Min[rt] += val;
lazy[rt] += val;
return;
}
push_down(rt);
int mid = (l + r) >> 1;
if(L <= mid) update(L, R, val, lson);
if(R > mid) update(L, R, val, rson);
push_up(rt);
}
pii query(int L, int R, int l, int r, int rt) {
if(L <= l && r <= R) {
return make_pair(Min[rt], num[rt]);
}
push_down(rt);
int mid = (l + r) >> 1;
pii tmp1 = make_pair(INF, 0), tmp2 = make_pair(INF, 0);
if(L <= mid) {
tmp1 = query(L, R, lson);
}
if(R > mid) {
tmp2 = query(L, R, rson);
}
pii ans;
if(tmp1.first == tmp2.first) {
ans = make_pair(tmp1.first, tmp1.second + tmp2.second);
return ans;
}
if(tmp1.first < tmp2.first) {
ans = tmp1;
return ans;
} else {
ans = tmp2;
return ans;
}
return ans;
}
int a[maxn];
pii Maxsta[maxn], Minsta[maxn];
map<int, int>mp;
int main() {
#ifndef ONLINE_JUDGE
FIN
#endif
int T;
scanf("%d", &T);
int cas = 1;
while(T--) {
int n;
int tpMax = 0;
int tpMin = 0;
scanf("%d", &n);
build(1, n, 1);
mp.clear();
LL ans = 0;
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
for(int i = 1; i <= n; i++) {
// mp[a[i]] = i;
while(tpMax > 0 && Maxsta[tpMax].first < a[i]) {
//减去小于当前值的Max贡献
update(Maxsta[tpMax - 1].second + 1, Maxsta[tpMax].second, a[i] - Maxsta[tpMax].first, 1, n, 1);
tpMax--;
}
Maxsta[++tpMax] = make_pair(a[i], i);
while(tpMin > 0 && Minsta[tpMin].first > a[i]) {
//减去大于当前值的Min贡献
update(Minsta[tpMin - 1].second + 1, Minsta[tpMin].second, Minsta[tpMin].first - a[i], 1, n, 1);
tpMin--;
}
Minsta[++tpMin] = make_pair(a[i], i);
//减去重复数字的贡献
update(mp[a[i]] + 1, i, -1, 1, n, 1);
mp[a[i]] = i;
pii flag = query(1, i, 1, n, 1);
if(flag.first == -1) {
ans += flag.second;
}
}
printf("Case #%d: %lld\n", cas++, ans);
}
return 0;
}
2018宁夏邀请赛 L Continuous Intervals(单调栈+线段树)的更多相关文章
- 2018ICPC银川 L Continuous Intervals 单调栈 线段树
题意:给你一个序列,问你这个序列有多少个子区间,满足把区间里的数排序之后相邻两个数之间的差 <= 1 ? 思路:https://blog.csdn.net/u013534123/article/ ...
- 2018宁夏邀请赛L Continuous Intervals
题目链接:https://nanti.jisuanke.com/t/28412 题意: 给出n个数的序列.问序列中有多少个区间满足,排序完之后任意两个相邻的数之差不大于1. 题解: 用max表示区间最 ...
- 2018宁夏邀请赛 L. Continuous Intervals
转化一下询问即为区间$max - min + 1 = cnt$,其中$cnt$表示区间内数的种类数. 即求有多少区间$max - min - cnt=-1$,注意到任意区间的$max-min-cnt ...
- 南昌邀请赛I.Max answer 单调栈+线段树
题目链接:https://nanti.jisuanke.com/t/38228 Alice has a magic array. She suggests that the value of a in ...
- 洛谷P4198 楼房重建 单调栈+线段树
正解:单调栈+线段树 解题报告: 传送门! 首先考虑不修改的话就是个单调栈板子题昂,这个就是 然后这题的话,,,我怎么记得之前考试好像有次考到了类似的题目昂,,,?反正我总觉着这方法似曾相识的样子,, ...
- 2018宁夏邀请赛 Continuous Intervals(单调栈 线段树
https://vjudge.net/problem/Gym-102222L 题意:给你n个数的序列,让判断有几个区间满足排完序后相邻两数差都不大于1. 题解:对于一个区间 [L,R],记最大值为 m ...
- 2019ICPC南昌邀请赛网络赛 I. Max answer (单调栈+线段树/笛卡尔树)
题目链接 题意:求一个序列的最大的(区间最小值*区间和) 线段树做法:用单调栈求出每个数两边比它大的左右边界,然后用线段树求出每段区间的和sum.最小前缀lsum.最小后缀rsum,枚举每个数a[i] ...
- The Preliminary Contest for ICPC China Nanchang National Invitational I. Max answer (单调栈+线段树)
题目链接:https://nanti.jisuanke.com/t/38228 题目大意:一个区间的值等于该区间的和乘以区间的最小值.给出一个含有n个数的序列(序列的值有正有负),找到该序列的区间最大 ...
- 2019南昌网络赛-I(单调栈+线段树)
题目链接:https://nanti.jisuanke.com/t/38228 题意:定义一段区间的值为该区间的和×该区间的最小值,求给定数组的最大的区间值. 思路:比赛时还不会线段树,和队友在这题上 ...
随机推荐
- poj2112 最大流
我用Dinic写的.G++ 1800ms 很慢,c++直接超时.优化后的 141ms,很快! 对于此题,建图方法很巧妙,通常想到求距离,那就会朝距离的方向建图,但是这题根据牛个数来建图,然后二分距离. ...
- C++模板编译模型
一:传统的编译模型 使用C/C++进行编程时,一般会使用头文件以使定义和声明分离,并使得程序以模块方式组织.将函数声明.类的定义放在头文件中,而将函数实现以及类成员函数的定义放在独立的文件中. 但是对 ...
- UVa 10323 【数学】
UVa 10323 题目:计算阶乘在10000~6227020800之间的值,不在范围对应输出Under或者Over. 分析:简单题.数论.因为13!=6227020800,7!<10000&l ...
- 关于mybatis中llike模糊查询中#和$的使用
模糊查询: 工作中用到,写三种用法吧,第四种为大小写匹配查询 1. sql中字符串拼接 SELECT * FROM tableName WHERE name LIKE CONCAT(CONCAT('% ...
- qt中绘制文字
(1)颜色QPen (2) 字体QFont (3)位置与对齐 void CircleWidget::paintEvent(QPaintEvent *event) { QPainter painter( ...
- LeetCode70 Climbing Stairs
题目: You are climbing a stair case. It takes n steps to reach to the top. Each time you can either cl ...
- Codeforces Round #591 (Div. 2, based on Technocup 2020 Elimination Round 1) 题解
A..B略 C 对当前的值排序,再二分答案,然后对于(i%x==0 && i%y==0)放入大的,再放其他的贪心解决即可. #include<iostream> #incl ...
- OracleSpatial函数实例
Oracle Spatial操作geometry方法 Oracle Spatial中SDO_GEOMETRY类型: CREATE TYPE SDO_GEOMETRY AS OBJECT( SDO_ ...
- Python语言的特点
- H3C用Telnet登录