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.

1≤N≤3.5×1051≤Ai,Bi≤N

样例解释:

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;
}
				
时间: 2024-10-01 02:30:27

51NOD 1962 区间计数 单调栈+二分 / 线段树+扫描线的相关文章

【10.7校内测试】【队列滑窗】【2-sat】【贪心+栈二分+线段树(noip模拟好题)】【生日祭!】

比较好想的一道题,直接用队列滑窗,因为扫一遍往队列里加东西时,改变的只有一个值,开桶储存好就行了! #include<bits/stdc++.h> using namespace std; int n, k, r; inline int min(int a, int b) { return a > b ? b : a; } inline int max(int a, int b) { return a > b ? a : b; } int sum[200005], q[200005

51Nod—1174 区间中最大的数 线段树模版

在大佬们题解的帮助下算是看懂了线段树吧...在这mark下防一手转头就忘. #include<iostream> #include<stdio.h> using namespace std; struct ki { int m,l,r; }tree[40005]; int ans=-1,a[10005]; void build(int n,int l,int r) { tree[n].l=l; tree[n].r=r; if(l==r) { tree[n].m=a[l];retur

51nod 1962 区间计数(单调栈+二分)

维护两个单调递减的栈,当i加进栈,位置x的数弹出的时候,在另一个栈中找到和这个数一样大的数,计算贡献(x-靠右左端点)*(i-x). #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<algorithm> #define ll long long using namespace std; const int maxn=500010,in

BZOJ 4059 Cerc2012 Non-boring sequences 线段树+扫描线

题目大意:定义一个序列为[不无聊的]当且仅当这个序列的任意一个区间都存在一个数只出现过一次,给定一个序列,要求判断这个序列是否是[不无聊的] 定义lasti表示第i个元素上一次出现的位置(第一次出现则为0),nexti表示第i个元素下一次出现的位置(最后一次出现则为n+1),那么这个元素能成为某个区间仅出现一次的数,当且仅当这个区间的左端点在[lasti+1,i]之间,右端点在[i,nexti?1]之间 我们可以将区间的左右端点放在二维平面上,那么一个元素产生的贡献是一个矩形,我们要确定的是所有

【BZOJ-3110】K大数查询 整体二分 + 线段树

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6265  Solved: 2060[Submit][Status][Discuss] Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a

HDU4614Vases and Flowers 二分+线段树;

参考:https://blog.csdn.net/ophunter_lcm/article/details/9879495 题意: 有n个花瓶,有两种操作,1.从a开始放b朵花,有花的花瓶跳过,2.把a到b间的花全部拿下来. 思路: 线段树+二分 利用二分确定区间,这样就可以是线段树实现更简单的问题: 1)对区间进行全部设置为1的操作 2)对区间进行全部清零的操作 #include <iostream> #include <cstdio> #include <algorith

SPOJ 1043 Can you answer these queries I 求任意区间最大连续子段和 线段树

题目链接:点击打开链接 维护区间左起连续的最大和,右起连续的和.. #include <cstdio> #include <iostream> #include <algorithm> #include <string.h> #include <math.h> #include <vector> #include <map> using namespace std; #define N 50050 #define Lson

poj 2823 Sliding Window 单调队列或线段树

题目链接:http://poj.org/problem?id=2823 Sliding Window Time Limit: 12000MS   Memory Limit: 65536K Total Submissions: 38315   Accepted: 11350 Case Time Limit: 5000MS Description An array of size n ≤ 106 is given to you. There is a sliding window of size k

HDU 1542 Atlantis (线段树 + 扫描线 + 离散化)

Atlantis Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 8998    Accepted Submission(s): 3856 Problem Description There are several ancient Greek texts that contain descriptions of the fabled i