51NOD 1810 连续区间 分治 区间计数

1810 连续区间
基准时间限制:1.5 秒 空间限制:131072 KB 分值: 80
 
 
区间内所有元素排序后,任意相邻两个元素值差为1的区间称为“连续区间”
如:3,1,2是连续区间,但3,1,4不是连续区间
给出一个1~n的排列,求出有多少个连续区间
Input
一个数n(n<=1,000,000)
第二行n个数,表示一个1~n的排列
Output
一个数,表示有多少个连续区间
Input示例
5
2 1 5 3 4
Output示例
9
样例解释:
区间[1,1][2,2][3,3][4,4][5,5][1,2][4,5][3,4][1,5]为连续区间//[l,r]表示从第l个数到第r个数构成的区间

 
题解:
  分治计数
  先将序列划分为左右两边,只考虑左端点在左边部分,右端点在右边部分的答案
  也就是左边部分对右边部分答案的贡献
  可知
      Max{max(i),max(j)} - Min{min(i),min(j)} = j - i;
  共有4中互不影响的情况,其实这里分类讨论下就可以O(1)求出
#include <bits/stdc++.h>
inline long long read(){long long x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
using namespace std;
#define ls i<<1
#define rs ls | 1
#define mid ((ll+rr)>>1)
#define MP make_pair
typedef long long LL;
typedef unsigned long long ULL;
const long long INF = 1e18+1LL;
const double pi = acos(-1.0);
const int N = 1e6 + 10, M = 502, inf = 1e9;

LL ans,mx[N],mx1[N],mn[N],mn1[N],vis[N * 3];
int a[N],n;

void solve(int ll,int rr) {
    if(ll == rr) return ;
    int last1 = -1,last2 = inf;
    for(int i = mid; i >= ll; --i)
        mx[i] = max(last1,a[i]),last1 = mx[i],
        mn[i] = min(last2,a[i]),last2 = mn[i];
    last1 = -1,last2 = inf;
    for(int i = mid+1; i <= rr; ++i)
        mx1[i] = max(last1,a[i]),last1 = mx1[i],
        mn1[i] = min(last2,a[i]),last2 = mn1[i];

    for(int i = ll; i <= mid; ++i) {
        int tmp = i + mx[i] - mn[i];
        if(tmp > mid && tmp <= rr
           && mx[i] > mx1[tmp] && mn[i] < mn1[tmp]) ans+=1;
    }


    for(int i = mid+1; i <= rr; ++i) {
        int tmp = i - mx1[i] + mn1[i];
        if(tmp >= ll && tmp <= mid
           && mx1[i] > mx[tmp] && mn1[i] < mn[tmp]) ans+=1;
    }
    int l = mid+1,r = mid;
    for(int i = mid; i >= ll; --i) {
       while(r < rr && mx[i] > mx1[r+1]) ++r, vis[r+mn1[r]+n]++;
       while(l <= r && mn[i] < mn1[l]) vis[l+mn1[l]+n]--,++l;
       ans += vis[i+mx[i]+n];
    }
    while(l <= r) vis[l+mn1[l]+n]--,l++;

    l = mid, r = mid+1;
    for(int i = mid+1; i <= rr; ++i) {
        while(r > ll && mx1[i] > mx[r-1]) r--,vis[r-mn[r]+n]++;
        while(l >= r && mn[l] > mn1[i]) vis[l-mn[l]+n]--,l--;
        ans += vis[i-mx1[i]+n];
    }
    while(l >= r) vis[l-mn[l]+n]--,l--;
    solve(ll,mid),solve(mid+1,rr);
}
int main() {
    scanf("%d",&n);
    for(int i = 1; i <= n; ++i) a[i] = read();
    solve(1,n);
    printf("%lld
",ans+n);
    return 0;
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
原文地址:https://www.cnblogs.com/zxhl/p/7571066.html