ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

牛客小白月赛39D - 绝望(数论 + 数学规律 + 数据结构)

2022-03-27 14:32:45  阅读:165  来源: 互联网

标签:10 39D int 质数 tre lk 牛客 小白月赛


牛客小白月赛39D - 绝望(源地址自⇔牛客

目录


tag

⇔数论、⇔数学规律、⇔数据结构、⇔*1700左右

题意

给定一个长度为 \(N\) 的序列 \(a\) ,规定如下两项操作:

  • 输入 \(4\) 个整数 ” \(1\ L\ R\ X\) “ ,代表将所有 \(i \in [L,R]\) 修改为 \(i^x * a_i\) ,并输出修改后的 \([L,R]\) 区间中的质数数量;
  • 输入 \(3\) 个整数 ” \(2\ L\ R\) “ ,直接输出 \([L,R]\) 区间中的质数数量。

一共 \(Q\) 次询问。

一组样例,满足 \(1≤N≤2∗10 ^5,1≤Q≤5∗10 ^5\) ,且 \(1≤a_i ≤5∗10^5,0≤x≤10\) 。注意,在操作过程中可能出现 \(a_i\ge 5*10^5\) 。

思路

赛时小结

很显然的线段树题目,而 \(x\) 与质数的规律也很快的找到了,然而交了一发WA之后重新推导才发现漏掉了一些特殊情况……

正解

首先,很显然需要用到线段树和素数筛,原因略。

我们分析题意,可以归纳出以下几点特征:

  • 对于 \(i=1\) ,无论如何操作,不会改变原来数的特征;
  • 对于 \(i\) 为质数且 \(a_i=1,x=1\) 的情况,会将其改变成质数;
  • 对于 \(x=0\) 的情况,不会改变原有特征;
  • 其余所有情况,都会将区间内的质数变成合数。

依据前三条特征,容易发现需要对单点进行操作,故我们需要使用化区间修改为单点修改的思想。在修改操作中做出相应的特判:

  • 对于第一、三条特征,直接忽视;
  • 对于第二条特征,使用额外的 \(\tt{}mp\) 数组记录该点 \(i\) 是否为质数,使用额外的 \(\tt{}one\) 数组记录该点的值是否为 \(1\) 。

对于第四条特征,我们使用区间修改思想,直接将该区间的值置为 \(0\) ,并将额外数组 \(\tt{}one\) 置为 \(0\) 。

由于本题第四条特征占了绝对多数:可以计算,一个数至多经过两次操作就会变成合数,考虑最坏情况, \(N\) 个数字均需要处理,那么此时总单点操作的完整复杂度为 \(\mathcal{O}(2 * N*logN+Q*logN)\) ,平均时间复杂度即为 \(\mathcal{O}(Q*logN)\) ,所以时间复杂度可以通过。

AC代码

点击查看代码
//====================
#define int LL
const int N	= 1e6 + 7;
#define lk k << 1
#define rk k << 1 | 1
struct node{
	int l, r, w, one;
}tre[4 * N];
int n, q;
//====================
vector<int> prime;
map<int, int> mp; int v[N];
void Force() {
	int n = N - 5;
	for(int i = 2; i <= n; i ++) {
        if(v[i] == 0) {
            v[i] = i;
            prime.push_back(i);  
        }
        for (auto it : prime) {
        	if (it > v[i] || it > n / i) break;
        	v[i * it] = it;
        }
    }
    for (auto it : prime) mp[it] = 1;
}
void update(int k) {
	tre[k].w = tre[lk].w + tre[rk].w;
	tre[k].one = tre[lk].one | tre[rk].one;
}
void build(int l, int r, int k) {
	tre[k].l = l, tre[k].r = r;
	if (l == r) {
		int x; cin >> x;
		if (x == 1) tre[k].one = 1;
		tre[k].w = mp[x];
		return;
	}
	int m = (tre[k].l + tre[k].r) >> 1;
	build(l, m, lk), build(m + 1, r, rk);
	update(k);
}
int finds(int l, int r, int k) {
	if (l <= tre[k].l && tre[k].r <= r) return tre[k].w;
	int ans = 0;
	int m = (tre[k].l + tre[k].r) >> 1;
	if (l <= m) ans += finds(l, r, lk);
	if (m < r) ans += finds(l, r, rk);
	return ans;
}
void change_point(int l, int r, int x, int k) {
	if (x == 0) return;
	if (tre[k].w == 0 && tre[k].one == 0) return;
	if (tre[k].l == tre[k].r) { //单点修改
		int num = tre[k].l;
		if (num == 1) return;
		if (x == 1 && tre[k].one == 1 && mp[num] == 1) {
			tre[k].one = 0;
			tre[k].w = 1;
			return;
		}
		tre[k].one = tre[k].w = 0;
		return;
	}
	int m = (tre[k].l + tre[k].r) >> 1;
	if (l <= m) change_point(l, r, x, lk);
	if (m < r) change_point(l, r, x, rk);
	update(k);
}
void Solve() {
	cin >> n >> q;
	build(1, n, 1);
	for (int i = 1; i <= q; ++ i) {
		int op; cin >> op;
		if (op == 2) {
			int l, r; cin >> l >> r;
			cout << finds(l, r, 1) << endl;
		}else {
			int l, r, x; cin >> l >> r >> x;
			change_point(l, r, x, 1);
			cout << finds(l, r, 1) << endl;
		}
	}
}

错误次数

(赛时 3 次)未考虑全所有情况。


文 / WIDA
2022.03.27 成文
首发于WIDA个人博客,仅供学习讨论


标签:10,39D,int,质数,tre,lk,牛客,小白月赛
来源: https://www.cnblogs.com/WIDA/p/16062737.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有