牛客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月前
【刷题记录】尼科彻斯定理、数对、环形结构
【刷题记录】尼科彻斯定理、数对、环形结构
|
9月前
|
存储 算法
算法训练,牛客.判断是不是平衡二叉树 牛客.最大子矩阵两个数组的交集牛客.数组中两个字符串的最小距离
算法训练,牛客.判断是不是平衡二叉树 牛客.最大子矩阵两个数组的交集牛客.数组中两个字符串的最小距离
算法训练,牛客.判断是不是平衡二叉树 牛客.最大子矩阵两个数组的交集牛客.数组中两个字符串的最小距离
|
9月前
|
搜索推荐
【海贼王的数据航海】排序——概念|直接插入排序|希尔排序
【海贼王的数据航海】排序——概念|直接插入排序|希尔排序
46 0
|
10月前
|
测试技术
【一刷《剑指Offer》】面试题 9:斐波那契数列(扩展:青蛙跳台阶、矩阵覆盖)
【一刷《剑指Offer》】面试题 9:斐波那契数列(扩展:青蛙跳台阶、矩阵覆盖)
|
10月前
|
搜索推荐 算法
拒绝水文!八大排序(一)【适合初学者】直接插入排序和希尔排序
拒绝水文!八大排序(一)【适合初学者】直接插入排序和希尔排序
|
10月前
|
监控
代码随想录Day31 贪心06 T738 单调递增的数字 T968监控二叉树
代码随想录Day31 贪心06 T738 单调递增的数字 T968监控二叉树
67 0
|
10月前
|
算法 vr&ar
1611F - ATM and Students详细题解(*1800,线段树维护前缀和;双指针算法(思维))
1611F - ATM and Students详细题解(*1800,线段树维护前缀和;双指针算法(思维))
69 0
[leetcode] 1675. 数组的最小偏移量 | 思维贪心 | 大疆笔试题
[leetcode] 1675. 数组的最小偏移量 | 思维贪心 | 大疆笔试题
131 0
线段树与树状数组总结分析(可能是最终版)(上)
线段树与树状数组总结分析(可能是最终版)
62 0
|
机器学习/深度学习
线段树与树状数组总结分析(可能是最终版)(下)
线段树与树状数组总结分析(可能是最终版)
84 0