【Codeforces Round #122 (Div. 1)】Codeforces 193D Two Segments

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

Nick calls a pair of segments [a0, a1] and [b0, b1]
(1 ≤ a0 ≤ a1 < b0 ≤ b1 ≤ n) good if all their (a1 - a0 + b1 - b0 + 2)
elements, when sorted in ascending order, form an arithmetic
progression with a difference of 1. That is, when they sorted in
ascending order, the elements are in the form
{x, x + 1, x + 2, …, x + m - 1}, for some x and 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) can be represented
as a pair of segments, as [l, i] and [i + 1, r] (l ≤ i ≤ r). As all
these pairs consist of the same set of elements, they are considered
identical.

See the notes accompanying the sample tests for clarification. Input

The first line contains integer n (1 ≤ n ≤ 3·105) — the permutation
size. The second line contains n space-separated distinct integers pi,
(1 ≤ pi ≤ n). Output

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

Please, do not use the %lld specifier to read or write 64-bit integers
in С++. It is preferred to use the cin, cout streams or the %I64d
specifier.

考虑枚举数字区间的右端点r,维护i..r这一段数值由几个区间拼成,如果区间数为1或2,则可以累加进答案。
当加入一个数x时,如果i..x这一段中没有数在x旁边【是指在原数列中在x旁边】,相当于新开一个区间,那么区间数会加1。如果恰有一个,相当于拼在另一个区间旁边,区间数不变。如果两个都出现了,相当于合并了两个区间,区间数减1。不难发现,随着左端点i越来越小,加入的数越来越多,“在x旁边的数的个数”是单调变化的。
于是我们需要用一个数据结构,支持区间加1减1,以及询问1和2的个数。于是可以用线段树,记录最小值和次小值及其出现个数【合并的时候注意分类讨论】。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
int a[300010],p[300010],
mn1[4000010],c1[4000010],mn2[4000010],c2[4000010],tag[4000010],
n;
void down(int p)
{
    if (tag[p])
    {
        mn1[p]+=tag[p];
        mn2[p]+=tag[p];
        tag[p*2]+=tag[p];
        tag[p*2+1]+=tag[p];
        tag[p]=0;
    }
}
int calc(int x1,int x2,int y1,int y2,int &v,int &c)
{
    if (x1==x2)
    {
        v=x1;
        c=y1+y2;
        return 0;
    }
    if (x1<x2)
    {
        v=x1;
        c=y1;
        return 1;
    }
    v=x2;
    c=y2;
    return -1;
}
void up(int p)
{
    down(p);
    down(p*2);
    down(p*2+1);
    switch (calc(mn1[p*2],mn1[p*2+1],c1[p*2],c1[p*2+1],mn1[p],c1[p]))
    {
      case 0:
        calc(mn2[p*2],mn2[p*2+1],c2[p*2],c2[p*2+1],mn2[p],c2[p]);
        break;
      case 1:
        calc(mn2[p*2],mn1[p*2+1],c2[p*2],c1[p*2+1],mn2[p],c2[p]);
        break;
      case -1:
        calc(mn1[p*2],mn2[p*2+1],c1[p*2],c2[p*2+1],mn2[p],c2[p]);
        break;
    }
}
void add(int p,int L,int R,int l,int r,int x)
{
    down(p);
    if (L==l&&r==R)
    {
        tag[p]+=x;
        return;
    }
    int mid=(L+R)/2;
    if (r<=mid) add(p*2,L,mid,l,r,x);
    else
    {
        if (l>mid) add(p*2+1,mid+1,R,l,r,x);
        else
        {
            add(p*2,L,mid,l,mid,x);
            add(p*2+1,mid+1,R,mid+1,r,x);
        }
    }
    up(p);
}
int qry(int p,int L,int R,int l,int r)
{
    down(p);
    int ret=0;
    if (L==l&&r==R)
    {
        if (mn1[p]<=2) ret+=c1[p];
        if (mn2[p]<=2) ret+=c2[p];
        return ret;
    }
    int mid=(L+R)/2;
    if (r<=mid) return qry(p*2,L,mid,l,r);
    if (l>mid) return qry(p*2+1,mid+1,R,l,r);
    return qry(p*2,L,mid,l,mid)+qry(p*2+1,mid+1,R,mid+1,r);
}
void in(int p,int L,int R,int x)
{
    if (L==R)
    {
        mn1[p]=0;
        c1[p]=1;
        return;
    }
    down(p);
    int mid=(L+R)/2;
    if (x<=mid) in(p*2,L,mid,x);
    else in(p*2+1,mid+1,R,x);
    up(p);
}
int main()
{
    int i,j,k,q,x,y,z;
    LL ans=0;
    scanf("%d",&n);
    for (i=1;i<=n;i++)
      scanf("%d",&a[i]);
    for (i=1;i<=n;i++)
      p[a[i]]=i;
    memset(mn1,0x3f,sizeof(mn1));
    memset(mn2,0x3f,sizeof(mn2));
    for (i=1;i<=n;i++)
    {
        in(1,1,n,i);
        x=p[i]==1?n+1:a[p[i]-1];
        y=p[i]==n?n+1:a[p[i]+1];
        if (x<y) swap(x,y);
        if (y>i) add(1,1,n,1,i,1);
        else
        {
            if (x>i) add(1,1,n,y+1,i,1);
            else
            {
                add(1,1,n,x+1,i,1);
                add(1,1,n,1,y,-1);
            }
        }
        ans+=qry(1,1,n,1,i);
    }
    printf("%I64d\n",ans-n);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值