[Codeforces Round #122 (Div. 1)] -D Two segments

洛谷传送门
Codeforces传送门

题目描述

Nick has some permutation consisting of p p p integers from 1 1 1 to n n n . A segment [ l , r ] [l,r] [l,r] ( l ≤ r l\le r lr ) is a set of elements p i p_{i} pi satisfying l ≤ i ≤ r l\le i\le r lir .

Nick calls a pair of segments [ a 0 , a 1 ] [a_{0},a_{1}] [a0,a1] and [ b 0 , b 1 ] [b_{0},b_{1}] [b0,b1]( 1 ≤ a 0 ≤ a 1 ; b 0 ≤ b 1 ≤ n 1\le a_{0}\le a_{1};b_{0}\le b_{1}\le n 1a0a1;b0b1n ) good if all their ( a 1 − a 0 + b 1 − b 0 + 2 a_{1}-a_{0}+b_{1}-b_{0}+2 a1a0+b1b0+2) elements, when sorted in ascending order, form an arithmetic progression with a difference of 1 1 1 . That is, when they sorted in ascending order, the elements are in the form x , x + 1 , x + 2 , . . . , x + m − 1 {x,x+1,x+2,...,x+m-1} x,x+1,x+2,...,x+m1, for some x x x and m m m .

Your task is to find the number of distinct pairs of good segments in the given permutation. Two pairs of segments are considered distinct if the sets of elements contained in these pairs of segments are distinct. For example, any segment [ l , r ] [l,r] [l,r] $ (l<r) $can be represented as a pair of segments, as [ l , i ] [l,i] [l,i] and [ i + 1 , r ] [i+1,r] [i+1,r] ( l ≤ i ≤ r l\le i\le r lir ). As all these pairs consist of the same set of elements, they are considered identical.

See the notes accompanying the sample tests for clarification.

输入输出格式

输入格式:

The first line contains integer n n n ( 1 ≤ n ≤ 3 ⋅ 1 0 5 1\le n\le 3·10^{5} 1n3105 ) — the permutation size. The second line contains n n n space-separated distinct integers p i p_{i} pi , ( 1 ≤ p i ≤ n 1\le p_{i}\le n 1pin ).

输出格式:

Print a single integer — the number of good pairs of segments of permutation p p p .

Please, do not use the %lld specifier to read or write 64 − b i t 64-bit 64bit integers in С++. It is preferred to use the c i n cin cin, c o u t cout cout streams or the %I64d specifier.

输入输出样例

输入样例#1:
3
1 2 3
输出样例#1:
3
输入样例#2:
5
1 4 5 3 2
输出样例#2:
10
输入样例#3:
5
5 4 3 1 2
输出样例#3:
10

题目大意

给你一个 1 ∼ n 1\sim n 1n的排列, 求出有多少种方案选出两个区间 [ l 1 , r 1 ] [l_1, r_1] [l1,r1] [ l 2 , r 2 ] [l_2,r_2] [l2,r2]满足其中所有元素排序后为一个公差为一的等差数列, 两种方案不同当且仅当两个区间合并后中元素不同。

解题分析

奇妙的思路。

只考虑原序列显然行不通, 我们肯定会将元素从小到大或从大到小逐次插入, 但我们怎么维护?

我们发现每次插入一个元素, 对区间个数形成影响的只有其两端的两个元素。

那么我们从大到小维护 F i ( x ) ( x ∈ [ i , n ] ) F_i(x)(x\in [i,n]) Fi(x)(x[i,n]) [ i , x ] [i,x] [i,x]的区间个数, 设 m n , m x mn,mx mn,mx分别表示原序列中 i i i两旁的两个元素。

  • m x &lt; i mx&lt;i mx<i的时候, 显然新加入一个元素所有 [ i , x ] [i,x] [i,x]的区间个数会比 [ i + 1 , x ] [i+1,x] [i+1,x] 1 1 1 F i ( x ) = F i + 1 ( x ) + 1 F_i(x)=F_{i+1}(x)+1 Fi(x)=Fi+1(x)+1
  • m n &lt; i &lt; m x mn&lt;i&lt;mx mn<i<mx的时候, 如果 x &gt; m x x&gt;mx x>mx显然就不会增加区间个数, 所以 F i ( x ) = F i + 1 ( x ) + 1 ( x ∈ [ i , m x − 1 ] ) F_i(x)=F_{i+1}(x)+1(x\in[i,mx-1]) Fi(x)=Fi+1(x)+1(x[i,mx1]) F i ( x ) = F i + 1 ( x ) ( x ∈ [ m x , n ] ) F_i(x)=F_{i+1}(x)(x\in [mx,n]) Fi(x)=Fi+1(x)(x[mx,n])
  • i &lt; m n i &lt;mn i<mn的时候, 如果 x &lt; m n x&lt;mn x<mn那么仍然区间个数会 + 1 +1 +1, 当 m n &lt; x &lt; m x mn&lt;x&lt;mx mn<x<mx的时候等于贴在了一端, 所以个数不变, x &gt; m x x&gt;mx x>mx的时候相当于结合了两个区间, 区间个数 − 1 -1 1, 所以 F i ( x ) = F i + 1 ( x ) + 1 ( x ∈ [ i , m n − 1 ] ) F_i(x)=F_{i+1}(x)+1(x\in[i,mn-1]) Fi(x)=Fi+1(x)+1(x[i,mn1]) F i ( x ) = F i + 1 ( x ) ( x ∈ [ m n , m x − 1 ] ) F_i(x)=F_{i+1}(x)(x\in[mn,mx-1]) Fi(x)=Fi+1(x)(x[mn,mx1]) F i ( x ) = F i + 1 ( x ) − 1 ( x ∈ [ m x , n ] ) F_i(x)=F_{i+1}(x)-1(x\in[mx,n]) Fi(x)=Fi+1(x)1(x[mx,n])

实质上我们只需要线段树区间 + + +再查询权值 ≤ 2 \le2 2的点的个数, 这个我们维护一个最小值, 最小值的个数, 最小值 + 1 +1 +1的个数即可。

代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define ll long long
#define MX 300050
#define gc getchar()
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
int dat[MX], ind[MX];
struct Node {int mn, mcnt, m1cnt, tag;} tree[MX << 2];
int dot;
ll ans;
namespace SGT
{
	#define ls (now << 1)
	#define rs (now << 1 | 1)
	IN void pushup(R int now)
	{
		tree[now].mcnt = tree[now].m1cnt = 0;
		tree[now].mn = min(tree[ls].mn, tree[rs].mn);
		if(tree[ls].mn == tree[now].mn) tree[now].mcnt += tree[ls].mcnt, tree[now].m1cnt += tree[ls].m1cnt;
		if(tree[ls].mn == tree[now].mn + 1) tree[now].m1cnt += tree[ls].mcnt;
		if(tree[rs].mn == tree[now].mn) tree[now].mcnt += tree[rs].mcnt, tree[now].m1cnt += tree[rs].m1cnt;
		if(tree[rs].mn == tree[now].mn + 1) tree[now].m1cnt += tree[rs].mcnt;
	}
	void build(R int now, R int lef, R int rig)
	{
		if(lef == rig) return tree[now].mcnt = 1, void();
		int mid = lef + rig >> 1;
		build(ls, lef, mid), build(rs, mid + 1, rig);
		pushup(now);
	}
	IN void pushdown(R int now)
	{
		if(tree[now].tag)
		{
			tree[ls].tag += tree[now].tag, tree[ls].mn += tree[now].tag;
			tree[rs].tag += tree[now].tag, tree[rs].mn += tree[now].tag;
			tree[now].tag = 0;
		}
	}
	IN void modify(R int now, R int lef, R int rig, R int lb, R int rb, R int del)
	{
		if(lb > rb) return;
		if(lef >= lb && rig <= rb)
		{
			tree[now].mn += del;
			tree[now].tag += del;
			return;
		}
		pushdown(now);
		int mid = lef + rig >> 1;
		if(lb <= mid) modify(ls, lef, mid, lb, rb, del);
		if(rb >  mid) modify(rs, mid + 1, rig, lb, rb, del);
		pushup(now);
	}
	IN int query(R int now, R int lef, R int rig, R int lb, R int rb)
	{
		if(lef >= lb && rig <= rb)
		{
			if(tree[now].mn == 1) return tree[now].m1cnt + tree[now].mcnt;
			if(tree[now].mn == 2) return tree[now].mcnt;
			return 0;
		}
		pushdown(now);
		int mid = lef + rig >> 1; int ret = 0;
		if(lb <= mid) ret += query(ls, lef, mid, lb, rb);
		if(rb  > mid) ret += query(rs, mid + 1, rig, lb, rb);
		return ret;
	}
}
int main(void)
{
	int mn, mx, pos;
	in(dot); SGT::build(1, 1, dot);
	for (R int i = 1; i <= dot; ++i) in(dat[i]), ind[dat[i]] = i;
	for (R int i = dot; i; --i)
	{
		mn = MX, mx = 0;
		pos = ind[i];
		if(pos > 1) mn = min(mn, dat[pos - 1]), mx = max(mx, dat[pos - 1]);
		if(pos < dot) mn = min(mn, dat[pos + 1]), mx = max(mx, dat[pos + 1]);
		if(mx < i) SGT::modify(1, 1, dot, i, dot, 1);
		else if(mn < i && i < mx) SGT::modify(1, 1, dot, i, mx - 1, 1);
		else
		{
			SGT::modify(1, 1, dot, i, mn - 1, 1);
			if(mn ^ mx)
			SGT::modify(1, 1, dot, mx, dot, -1);
		}
		ans += SGT::query(1, 1, dot, i, dot);
	}
	printf("%I64d", ans - dot);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值