二维数点 bzoj 3956 Count

https://www.lydsy.com/JudgeOnline/problem.php?id=3956

70分好像可以莫队

首先要发现答案是\(\mathcal O(n)\)的

Proof:

考虑一个点\(a_k\)对区间\([l,r]\)的贡献
当且仅当\(a_k=max(a_{l+1}, ... , a_{r-1})\)且\(a_l>a_k\), \(a_k<a_r\)

于是对于一个点预处理一下\((x, y)\)左边比他大的\(x\), 右边比他大的\(y\)

重复的数字的话 答案就只由最左边的更新就好了

统计答案主席树二维数点就好了

学习到了主席树的写法

先拷贝之前的根 然后在update时候每次新建一个节点

#include <bits/stdc++.h>
#define fo(i, n) for(int i = 1; i <= (n); i ++)
#define out(x) cerr << #x << " = " << x << "\n"
using namespace std;
// by piano
template<typename tp> inline void read(tp &x) {
  x = 0; char c = getchar(); bool f = 0;
  for(; c < '0' || c > '9'; f |= (c == '-'), c = getchar());
  for(; c >= '0' && c <= '9'; x = (x << 3) + (x << 1) + c - '0', c = getchar());
  if(f) x = -x;
}
template<typename tp> inline void arr(tp *a, int n) {
  for(int i = 1; i <= n; i ++)
    cout << a[i] << " ";
  puts("");
}
const int N = 3e5 + 233;
int n, m, type;
int a[N], q[N], top = 0;
int LP[N], RP[N], LT[N], rt[N];
vector<int> vec[N];

namespace seg {
#define mid (l + (r - l) / 2)
  const int Segment_Size = N * 20;
  int sum[Segment_Size], L[Segment_Size], R[Segment_Size];
  int tot = 0;
  inline void add(int &rt, int pre, int x, int l, int r) {
    rt = ++ tot;
    sum[rt] = sum[pre];
    ++ sum[rt];
    if(l == r) return ;
    if(x <= mid) R[rt] = R[pre], add(L[rt], L[pre], x, l, mid);
    else L[rt] = L[pre], add(R[rt], R[pre], x, mid + 1, r);
  }
  inline int query(int v1, int v2, int ql, int qr, int l, int r) {
    if(!v2) return 0;
    if(ql == l && qr == r) return sum[v2] - sum[v1];
    if(qr <= mid) return query(L[v1], L[v2], ql, qr, l, mid);
    else if(ql > mid) return query(R[v1], R[v2], ql, qr, mid + 1, r);
    else return query(L[v1], L[v2], ql, mid, l, mid) + query(R[v1], R[v2], mid + 1, qr, mid + 1, r);
  }
  inline void dfs(int rt, int l, int r) {
    if(!rt || !sum[rt]) return ;
    cout << l << " " << r << " " << sum[rt] << "\n";
    if(l == r) return ;
    dfs(L[rt], l, mid);
    dfs(R[rt], mid + 1, r);
  }
#undef mid
}

int main(void) {
  read(n); read(m); read(type);
  for(int i = 1; i <= n; i ++)
    read(a[i]);
  // LP
  top = 0;
  for(int i = 1; i <= n; i ++) {
    while(top && a[q[top]] <= a[i]) -- top;
    if(top) LP[i] = q[top];
    q[++ top] = i;
  }
  // RP
  top = 0;
  for(int i = n; i >= 1; i --) {
    while(top && a[q[top]] <= a[i]) -- top;
    if(top) RP[i] = q[top];
    q[++ top] = i;
  }
  // LT
  top = 0;
  for(int i = 1; i <= n; i ++) {
    while(top && a[q[top]] < a[i]) -- top;
    if(top) LT[i] = q[top];
    q[++ top] = i;
  }

  for(int i = 1; i <= n; i ++)
    if(LP[i] && RP[i] && LP[i] == LT[i])
      vec[LP[i]].push_back(RP[i]);

  for(int i = 1; i <= n; i ++) {
    rt[i] = rt[i - 1];
    for(int k = 0; k < (int) vec[i].size(); k ++) {
      seg::add(rt[i], rt[i], vec[i][k], 1, n);
    }
  }
  int ans = 0;
  for(int i = 1; i <= m; i ++) {
    int l, r; read(l); read(r);
    if(type) {
      int u = (l + ans - 1) % n + 1;
      int v = (r + ans - 1) % n + 1;
      l = min(u, v);
      r = max(u, v);
    }
    ans = seg::query(rt[l - 1], rt[r], l, r, 1, n);
    ans += r - l;
    cout << ans << "\n";
  }
}

原文地址:https://www.cnblogs.com/foreverpiano/p/9210553.html

时间: 2024-10-09 09:05:59

二维数点 bzoj 3956 Count的相关文章

【一类题】二维数点的几个做法

二维数点的题目还用赘述么…… 题意就是这道题 离线 $CDQ$ 分治 在线 $k-d tree$ 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/two-dimensional.html

loj #535. 「LibreOJ Round #6」花火 树状数组求逆序对+主席树二维数点+整体二分

$ \color{#0066ff}{ 题目描述 }$ 「Hanabi, hanabi--」 一听说祭典上没有烟火,Karen 一脸沮丧. 「有的哦-- 虽然比不上大型烟花就是了.」 还好 Shinobu 早有准备,Alice.Ayaya.Karen.Shinobu.Yoko 五人又能继续愉快地玩耍啦! 「噢--!不是有放上天的烟花嘛!」Karen 兴奋地喊道. 「啊等等--」Yoko 惊呼.Karen 手持点燃引信的烟花,「嗯??」 Yoko 最希望见到的是排列优美的烟火,当然不会放过这个机会-

bzoj 3956: Count

3956: Count Description Input Output Sample Input 3 2 0 2 1 2 1 1 1 3 Sample Output 0 3 HINT M,N<=3*10^5,Ai<=10^9 Source CH Round#64 MFOI杯水题欢乐赛day1 By Gromah 题解: 性质很妙的一道题. 首先可以发现这个所有的点队数是很小的,考虑以把一个点作为两个端点中较小的一个,那么另一个端点肯定是向左向右第一个大于等于它的点,这是很显然的.... 我们

静态二维数点问题离线解法的一种设想

先将所有点和询问读下来,按每一维离散化. 之后开n个vector,表示横坐标为i的点.可以\(O(n)\)处理二维前缀和. 查询时在vector中lower_bound,差分即可. 总时间复杂度\(O(n\log n)\),空间复杂度\(O(n)\),常数可能比扫描线小. 原文地址:https://www.cnblogs.com/utopia999/p/9813330.html

$[SHOI2007]$ 园丁的烦恼 二维数点/树状数组

\(Sol\) 设一个矩阵的左上角为\((x_1,y_1)\),右下角为\((x_2,y_2)\),\(s_{x,y}\)是到\((1,1)\)二维前缀和,那么这个矩阵的答案显然是\(s_{x_2,y_2}-s_{x_1-1,y_2}-s_{x_2,y_1-1}+s_{x_1-1,x_2-1}\).考虑把每个询问拆成这么四个二维前缀和的询问.将所有询问的\(s_{x,y}\)按照\(x\)排序,依次计算,这样我们就可以忽略\(x\)的限制而只考虑\(y\)的限制了.每次扫到一个询问,先把\(x\

【BZOJ4785】[Zjoi2017]树状数组 树套树(二维线段树)

[BZOJ4785][Zjoi2017]树状数组 Description 漆黑的晚上,九条可怜躺在床上辗转反侧.难以入眠的她想起了若干年前她的一次悲惨的OI 比赛经历.那是一道基础的树状数组题.给出一个长度为 n 的数组 A,初始值都为 0,接下来进行 m 次操作,操作有两种: 1 x,表示将 Ax 变成 (Ax + 1) mod 2. 2 l r,表示询问 sigma(Ai) mod 2,L<=i<=r 尽管那个时候的可怜非常的 simple,但是她还是发现这题可以用树状数组做.当时非常yo

CF1221F Choose a Square(二维偏序)

由于y=x,我们可以将点对称过来,以便(x,y)(x<y) 考虑选取正方形(a,a,b,b),点集则为\((a\le x\le y\le b)\),相当于二维数点 将点按x降序,y升序排列,线段树先存"-坐标",以便统计\((a,b)\)时直接线段树查询最大值再加b即可 原文地址:https://www.cnblogs.com/y2823774827y/p/11629629.html

BZOJ 1452: [JSOI2009]Count (二维树状数组)

Description Input Output Sample Input Sample Output 1 2 HINT 二维树状数组的简单应用,c数组的第一维坐标相当于哈希.如果是修改操作,修改前 将当前的值的个数以及祖先都减1, 修改后将个数加1. #include <iostream> #include <cstring> #include <cstdio> #include <cmath> #include <set> #include

BZOJ 1452: [JSOI2009]Count 二维树状数组

1452: [JSOI2009]Count Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=1452 Description Input Output Sample Input Sample Output 1 2 HINT 题意 题解: 裸的二维树状数组 代码: //qscqesze #include <cstdio> #include <cmath&g