A 找倍数 (线段树)
(“华讯杯”内蒙古自治区第十七届大学生程序设计竞赛 A题 找倍数)
题目描述
有一个下标从 1 到 n 的数列,需要进行 q 次操作。操作有两种类型:
- 修改操作(类型1):将下标在区间 [l, r] 中的每一个数加上 x
格式:1 l r x
- 查询操作(类型2):求出下标在区间 [l, r] 内的数中有多少是 y 的倍数(保证 y 是 m 的因子)
格式:2 l r y
输入格式:
第一行包含三个整数 n , q , m n, q, m n,q,m ( 1 ≤ n , q ≤ 100 , 000 , 1 ≤ m ≤ 50 1 \leq n, q \leq 100,000, 1 \leq m \leq 50 1≤n,q≤100,000,1≤m≤50 ),分别表示数列长度、操作次数和常数 m m m 。
第二行包含 n n n 个用空格分隔的整数,表示数列的初始值 a i a_i ai( 0 ≤ a i ≤ m 0 \leq a_i \leq m 0≤ai≤m )。
接下来 q q q 行,每行描述一个操作:四个整数,第一个是操作类型(1 或 2),后三个是操作参数(含义如上)。
输出格式:
对于每个类型 2 的操作,输出一行整数,表示查询的结果。
输入样例:
5 6 10
1 2 4 6 8
2 1 5 2
1 1 5 1
2 3 5 2
2 1 3 2
1 5 5 1
2 1 5 10
输出样例:
4
0
1
1
思路
区间问题使用线段树维护:
m 的值不超过 50,因此因子 y 的取值只有少数几个。
每个线段树节点维护一个数组 cnt[MAXM],其中 cnt[i] 表示该节点对应区间内值 ≡ i (mod m) 的元素个数。
区间加法操作相当于对区间内所有数的模 m 结果进行平移((i + x) % m)。
查询时,直接累加所有 i ≡ 0 (mod y) 的 cnt[i] 值。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define endl '\n'
#define pb push_back
#define pii pair<int, int>
#define FU(i, a, b) for (int i = (a); i <= (b); ++i)
#define FD(i, a, b) for (int i = (a); i >= (b); --i)
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const int maxn = 1e5 + 5, MAXN = maxn;
const int maxm = 50;
int n, m, q;
int a[maxn];
struct node {int lz = 0;int l, r;int cnt[maxm] = {};
} tr[4 * maxn];void pushup(int p) {for (int i = 0; i < maxm; i++) {tr[p].cnt[i] = tr[2 * p].cnt[i] + tr[2 * p + 1].cnt[i];}
}void apply(int p, int x) {x %= m;int tcnt[maxm] = {};for (int i = 0; i < m; i++) {int j = (i + x) % m;tcnt[j] += tr[p].cnt[i];}memcpy(tr[p].cnt, tcnt, sizeof(int) * m);tr[p].lz = (tr[p].lz + x) % m;
}void pushdown(int p) {if (tr[p].lz) {if (tr[p].l != tr[p].r) {apply(2 * p, tr[p].lz);apply(2 * p + 1, tr[p].lz);tr[p].lz = 0;}}
}void build(int p, int l, int r) {tr[p].l = l;tr[p].r = r;tr[p].lz = 0;memset(tr[p].cnt, 0, sizeof(tr[p].cnt));if (l == r) {tr[p].cnt[(a[l] % m)] = 1;return;}int mid = (l + r) / 2;build(2 * p, l, mid);build(2 * p + 1, mid + 1, r);pushup(p);
}void add(int p, int l, int r, int x) {if (x % m == 0)return;if (l <= tr[p].l && tr[p].r <= r) {apply(p, x);return;}pushdown(p);int mid = (tr[p].l + tr[p].r) / 2;if (mid >= l) {add(2 * p, l, r, x);}if (mid < r) {add(2 * p + 1, l, r, x);}pushup(p);
}int ask(int p, int l, int r, int y) {if (l <= tr[p].l && tr[p].r <= r) {int sum = 0;for (int i = 0; i < m; i += y) {sum += tr[p].cnt[i];}return sum;}pushdown(p);int mid = (tr[p].l + tr[p].r) / 2;int res = 0;if (mid >= l) {res += ask(2 * p, l, r, y);}if (mid < r) {res += ask(2 * p + 1, l, r, y);}return res;
}signed main() {
#ifndef ONLINE_JUDGEfreopen("../in.txt", "r", stdin);
#endifcin.tie(0)->ios::sync_with_stdio(0);cin >> n >> q >> m;FU(i, 1, n) { cin >> a[i]; }build(1, 1, n);while (q--) {int op, l, r, y;cin >> op >> l >> r >> y;if (op == 1) {add(1, l, r, y);} else {cout << ask(1, l, r, y) << endl;}}return 0;
}