51NOD 1962 区间计数 单调栈+二分 / 线段树+扫描线

 区间计数
 
基准时间限制:1.5 秒 空间限制:262144 KB 分值: 80
 

两个数列 {An} , {Bn} ,请求出Ans, Ans定义如下:

 

Ans:=Σni=1Σnj=i[max{Ai,Ai+1,...,Aj}=max{Bi,Bi+1,...,Bj}] 

 

注:[ ]内表达式为真,则为1,否则为0.

 
 
1N3.5×1051Ai,BiN 
 
样例解释:
7个区间分别为:(1,4),(1,5),(2,4),(2,5),(3,3),(3,5),(4,5)
Input
第一行一个整数N
第二行N个整数Ai
第三行N个整数Bi
Output
一行,一个整数Ans
Input示例
5
1 4 2 3 4
3 2 2 4 1
Output示例
 
7
 
题解:
  第一种做法是 两个单调栈 + 二分
  两个数都同时维护一个单调递减的 栈
  当元素出栈是我们可以确定以其为最大值所能掌管的一段区间,那么 在对应另一个栈内通过二分找到此值也能掌管一段区间
  求出区间交就可以了
  第二种做法比较类似
  枚举固定最大值为x 的区间有哪些
  在A数组中 假设x 所在位置为  i   ,利用单调栈可以求出 其掌管范围 [L1,R1], 对应的
  在B数组中 假设x 所在位置为  i   ,利用单调栈可以求出 其掌管范围 [L2,R2],
  那么抽出 [L1,i] [i,R1],  [L2,i] [i,R2], 在二维坐标系上就是两个矩阵,求出矩阵面积交就可以了
#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 = 4e5 + 10, M = 1e3, inf = 2e9;

int n,a[N],b[N];
int l[2],r[2],q[2][N],pos[2][N];
LL cal(int x,int p,int i) {
     int ll = l[p], rr = r[p],ok = ll;
     while(ll <= rr) {
        if(q[p][mid] > x) {
            ll = mid + 1;
        } else ok = mid,rr = mid - 1;
    }
  //  cout<<i<<" "<<p<<" "<<ok<<" "<<q[p][ok]<<" "<<x<<endl;
    int mmp1,mmp2;
    mmp1 = max(pos[p][ok-1]+1,pos[p^1][r[p^1]-1]+1);
    mmp2 = i-1;

    if(q[p][ok] == x)
        return 1LL * max(min(pos[p^1][r[p^1]],pos[p][ok]) - mmp1+1,0)
        * max(mmp2 - max(pos[p^1][r[p^1]],pos[p][ok]) + 1,0);
    else return 0;
}
int main() {
    cin >> n;
    for(int i = 1; i <= n; ++i) scanf("%d",&a[i]);
    for(int i = 1; i <= n; ++i) scanf("%d",&b[i]);
    l[0] = l[1] = 1;
    r[0] = r[1] = 0;
    pos[0][0] = pos[1][0] = 0;
    LL ans = 0;
    for(int i = 1; i <= n; ++i) {
        LL ret = 0;
        while(l[0] <= r[0] && a[i] >= q[0][r[0]]) {
            ret += cal(q[0][r[0]],1,i);
            r[0]--;
        }
        q[0][++r[0]] = a[i];pos[0][r[0]] = i;
        while(l[1] <= r[1] && b[i] >= q[1][r[1]]) {
            ret += cal(q[1][r[1]],0,i);
            r[1]--;
        }
        q[1][++r[1]] = b[i];pos[1][r[1]] = i;
        //cout<<"fuck "<<i<<" "  << ret<<endl;
        ans += ret;
    }
    while(l[0] <= r[0]) {
        ans += cal(q[0][r[0]],1,n+1);
        r[0]--;
    }
    cout<<ans<<endl;
    return 0;
}

 

 
  

转载于:https://www.cnblogs.com/zxhl/p/7596793.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值