牛客xiao白月赛39 D(线段树维护区间)

简介: 牛客xiao白月赛39 D(线段树维护区间)


我没有打这个比赛,但是队友让我做了这个题(确实是好题),这次做的这个题目让我的线段树的理解获得了极大的提升,以往我的思维想的是:

pushup维护递归到子树后子树传递给父亲的信息。

pushdown维护父亲将要对子树的影响的信息。

回到这个题目:

通过分析题意结合唯一分解定理易知:

1.如果x==0 修改后的A[i]=ai,等于本身,即修改后原先是质数,那么修改后仍然是质数,原先不是质数,修改后仍不是质数。

2.如果x>1, 修改后的A[i]=p*ai(p>=1)。

1.如果p>1 那么ai修改后一定为合数。

2.如果p=1,那么ai修改后不变,他仍然是之前的数(不是质数或是质数)。

3 如果x=1,修改后的A[i]=i*ai,如果ai不为1,那么一定变合数;如果ai为1,A[i]变合数还是质数和i有关,i为质数即A[i]为质数,反之合数

在此,所有的情况就分析完了,接下来分析怎么样实现代码和改使用什么数据结构维护这些查询

暴力模拟,暴力啊!暴力出奇迹,看到 n m是1e5的级别这时候如果想暴力就没有写下去的必要了!

这时候问自己,什么能够维护区间?什么能在 mongn时间复杂度时间内优雅跑出答案?,答案呼之欲出!!!线段树

再想想实现方法

1.我们要的不是这个a[i]这个数的值,这个数只用一个用,他是否为质数,或者不是,我们要的是质数的总和,那么是质数标记1就行了,不是质数标记成0?,还不够!

2.本题有某个点质数和合数一直不变的情况,即a[1]的情况,a[i]无论x为什么a[i]都不会变,因为i==1;本题有非质数转化为合数的情况,如上文所提,i!=1&&a[i]==1时,a[i]变成质数和合数取决于i的是质数还是合数,而且他变了这一次后,便失去的这个特殊性

3.所以我们还要想办法将非质数转变成质数,这个==懒标记lz用的十分精髓,而且我从来没做过子树向父亲传递懒标记的题目,这个题目的懒标记用的十分6:

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
struct node {
  int l;
  int r;
  int sum;// 1 质数 0 合数
  int lz; //1 质数  0 合数  2 是非质数(为1)但是可能变质数
} tr[N << 2];
int l, r, x, op;
int n, m;
bool f[N * 3];
int a[N];
void init() {//埃式筛
  f[1] = true;
  for (int i = 2; i <= 5e5 + 10; i++) {
    for (int j = i * 2; j <= 5e5 + 10; j += i) {
      f[j] = true;
    }
  }
}
void pushup(int u) {//向父亲传递影响
  if (tr[u << 1].lz == 0 && tr[u << 1 | 1].lz == 0) tr[u].lz = 0;//如果左区间和右区间都为合数,这个父亲区间再也不会有质数了,打一个标签 
  else tr[u].lz = 2;//这个区间可能有1变成质数的情况,要向下递归看
  tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;//这个区间的质数和=左树区间质数和+右树区间质数和
}
void build(int u, int l, int r) {//递归建树
  tr[u].l = l;
  tr[u].r = r;
  if (l == r) {
    //Tr[u].lz=is[a[l]];
    if (a[l] == 1 && l != 1)  tr[u].lz = 2;//如果a[i]=1,而且i!=1,那么它保留了变成质数的可能
    else if (!f[a[l]])  tr[u].lz = 1; //如果为质数 标记1
    else        tr[u].lz = 0;//合数标记 0
    if (tr[u].lz == 1)  tr[u].sum = 1; //0现在是质数  1不是质数.
    else      tr[u].sum = 0;
    return;
  }
  int mid = l + r >> 1;
  build(u << 1, l, mid);
  build(u << 1 | 1, mid + 1, r);
  pushup(u);
}
void update(int u, int l, int r, int k) {
  if (tr[u].lz == 0) return;//如果这个区间没有质数了,直接返回
  if (tr[u].l >= l && tr[u].r <= r) {
    if (tr[u].l == tr[u].r) {
      if (k > 1) { //所有质数变合数,合数还是合数,除a[1]的情况
        if (tr[u].l == 1 && tr[u].lz == 1) { a[1]是质数,那么它永远为质数,是合数则永远为合数
          tr[u].sum = 1;
          tr[u].lz = 1;
        } else {//其他质数全变合数
          tr[u].sum = 0;
          tr[u].lz = 0;
        }
      } else { // 质变和  为一的数可能会变质数
        if (a[tr[u].l] == 1 || tr[u].l == 1) {  //a[i]=1的情况,可能变质数
          a[tr[u].l] *= tr[u].l;
          if (!f[a[tr[u].l]]) tr[u].sum = 1, tr[u].lz = 1;
          else      tr[u].sum = 0, tr[u].lz = 0;
        } else {
          //其他质数全变合数
          if (tr[u].lz == 1)  tr[u].sum = 0, tr[u].lz = 0;
        }
      }
    }
     return;
  }
  int mid = tr[u].l + tr[u].r >> 1;
  if (l <= mid) update(u << 1, l, r, k);
  if (r > mid) update(u << 1 | 1, l, r, k);
  pushup(u);
}
int query(int u, int l, int r) {
  if (tr[u].l >= l && tr[u].r <= r) {
    return tr[u].sum;
  }
  int mid = tr[u].l + tr[u].r >> 1;
  int s = 0;
  if (l <= mid) s += query(u << 1, l, r);
  if (r > mid) s += query(u << 1 | 1, l, r);
  return s;
}
int main() {
  init();
  scanf("%d%d", &n, &m);
  for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
  build(1, 1, n);
  while (m--) {
    scanf("%d", &op);
    if (op == 1) {
      scanf("%d%d%d", &l, &r, &x);
      if (x != 0) update(1, l, r, x);//x=0,更新等于没有更新
      cout << query(1, l, r) << "\n";
    } else {
      scanf("%d%d", &l, &r);
      cout << query(1, l, r) << "\n";
    }
  }
}

完结撒花

目录
相关文章
|
7月前
【每日一题Day179】LC1157子数组中占绝大多数的元素 | 线段树
【每日一题Day179】LC1157子数组中占绝大多数的元素 | 线段树
54 0
|
6月前
每日一题 540. 有序数组中的单一元素
每日一题 540. 有序数组中的单一元素
|
6月前
|
存储 算法
算法训练,牛客.判断是不是平衡二叉树 牛客.最大子矩阵两个数组的交集牛客.数组中两个字符串的最小距离
算法训练,牛客.判断是不是平衡二叉树 牛客.最大子矩阵两个数组的交集牛客.数组中两个字符串的最小距离
算法训练,牛客.判断是不是平衡二叉树 牛客.最大子矩阵两个数组的交集牛客.数组中两个字符串的最小距离
|
7月前
|
算法
DAY-7 | 牛客-BM21 寻找旋转数组的最小元素:二分法分治思想真的很可靠
这是一个关于编程题目的摘要:题目是牛客网上的&quot;BM21 旋转数组的最小数字&quot;,要求找到旋转数组中的最小数字。题解介绍了使用二分查找算法来解决此问题,因为其时间复杂度优于暴力搜索的线性时间复杂度。二分查找的核心是通过比较中间元素与右端元素的大小,不断缩小搜索范围,最终找到最小值。代码示例展示了如何实现这个算法。总结中强调了二分查找适用于部分有序数组,并指出了解决这类问题的关键在于理解数组的二段单调性。
47 1
|
7月前
|
测试技术
【一刷《剑指Offer》】面试题 9:斐波那契数列(扩展:青蛙跳台阶、矩阵覆盖)
【一刷《剑指Offer》】面试题 9:斐波那契数列(扩展:青蛙跳台阶、矩阵覆盖)
|
7月前
|
监控
代码随想录Day31 贪心06 T738 单调递增的数字 T968监控二叉树
代码随想录Day31 贪心06 T738 单调递增的数字 T968监控二叉树
52 0
|
搜索推荐 算法 C语言
手撕排序算法3:优化版冒泡排序和快速排序的三种方法(包括三数取中,小区间优化,递归版本)(上)
手撕排序算法3:优化版冒泡排序和快速排序的三种方法(包括三数取中,小区间优化,递归版本)(上)
手撕排序算法3:优化版冒泡排序和快速排序的三种方法(包括三数取中,小区间优化,递归版本)(上)
|
搜索推荐 算法
手撕排序算法3:优化版冒泡排序和快速排序的三种方法(包括三数取中,小区间优化,递归版本)(下)
手撕排序算法3:优化版冒泡排序和快速排序的三种方法(包括三数取中,小区间优化,递归版本)(下)
|
算法
代码随想录算法训练营第二十天 | LeetCode 530. 二叉搜索树的最小绝对差、501. 二叉搜索树中的众数、236. 二叉树的最近公共祖先
代码随想录算法训练营第二十天 | LeetCode 530. 二叉搜索树的最小绝对差、501. 二叉搜索树中的众数、236. 二叉树的最近公共祖先
52 0
|
机器学习/深度学习 存储 算法
【数据结构与算法篇】手撕八大排序算法之快排的非递归实现及递归版本优化(三路划分)
【数据结构与算法篇】手撕八大排序算法之快排的非递归实现及递归版本优化(三路划分)
232 0