文章目录
P3372 【模板】线段树 1
区间修改和区间求和
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
const int N = 1e5 + 10;
typedef long long ll;
ll a[N];
struct Node {
int l, r;
ll sum, maxv, lazy;
void updade(ll x) {
sum += (r - l + 1) * x;
maxv += x;
lazy += x;
}
} tree[N * 4];
void build(int p, int l, int r)
{
tree[p].l = l, tree[p].r = r;
tree[p].sum = tree[p].lazy = 0;
if (l == r) {
tree[p].sum = tree[p].maxv = a[l];
return;
}
int mid = l + r >> 1;
build(p << 1, l , mid);
build(p << 1 | 1, mid + 1, r);
tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum;
tree[p].maxv = max(tree[p << 1].maxv, tree[p << 1 | 1].maxv);
}
void spread(int p)
{
int lazy_val = tree[p].lazy;
if (lazy_val) {
tree[p << 1].updade(lazy_val);
tree[p << 1 | 1].updade(lazy_val);
tree[p].lazy = 0;
}
}
void update(int p, int l, int r, int val)
{
if (l <= tree[p].l && tree[p].r <= r) {
tree[p].updade(val);
} else {
spread(p);
int mid = tree[p].l + tree[p].r >> 1;
if (l <= mid)
update(p << 1, l, r, val);
if (r > mid)
update(p << 1 | 1, l, r, val);
tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum;
tree[p].maxv = max(tree[p << 1].maxv , tree[p << 1 | 1].maxv);
}
}
ll query(int p, int l, int r)
{
if (l <= tree[p].l && tree[p].r <= r)
return tree[p].sum;
spread(p);
ll ans = 0;
int mid = tree[p].l + tree[p].r >> 1;
if (l <= mid)
ans += query(p << 1, l, r);
if (r > mid)
ans += query(p << 1 | 1, l, r);
return ans;
}
int main()
{
int n, q, l, r, val, opt;
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; ++i)
scanf("%lld", a + i);
build(1, 1, n);
while (q--) {
cin >> opt;
if (opt == 2) {
scanf("%d %d", &l, &r);
printf("%lld\n", query(1, l, r));
} else {
scanf("%d %d %d", &l, &r, &val);
update(1, l, r, val);
}
}
return 0;
}
P3373 【模板】线段树 2
先乘再加,一开始把更新乘和更新加分开了,一直WA
合起来之后就AC了
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
const int N = 1e5 + 10;
typedef long long ll;
ll a[N];
int M;
struct Node {
ll l, r;
ll sum, maxv, add_lazy, mul_lazy;
// 更新 sum 和懒标
void updade(int add, int mul) {
sum = (sum * mul % M + add * (r - l + 1) % M) % M;
mul_lazy = mul_lazy * mul % M;
add_lazy = (add_lazy * mul % M + add) % M;
}
} tree[N * 4];
void push_up(int p)
{
tree[p].sum = (tree[p << 1].sum + tree[p << 1 | 1].sum) % M;
tree[p].maxv = max(tree[p << 1].maxv, tree[p << 1 | 1].maxv);
}
void build(int p, int l, int r)
{
tree[p].l = l, tree[p].r = r;
tree[p].sum = tree[p].add_lazy = 0;
tree[p].mul_lazy = 1;
if (l == r) {
tree[p].sum = tree[p].maxv = a[l] % M;
return;
}
int mid = l + r >> 1;
build(p << 1, l , mid);
build(p << 1 | 1, mid + 1, r);
push_up(p);
}
void spread(int p)
{
// 先乘再加
int mul = tree[p].mul_lazy, add = tree[p].add_lazy;
tree[p << 1].updade(add, mul);
tree[p << 1 | 1].updade(add, mul);
tree[p].add_lazy = 0;
tree[p].mul_lazy = 1;
}
void update(int p, int l, int r, int add, int mul)
{
if (l <= tree[p].l && tree[p].r <= r) {
tree[p].updade(add, mul);
} else {
spread(p);
int mid = tree[p].l + tree[p].r >> 1;
if (l <= mid)
update(p << 1, l, r, add, mul);
if (r > mid)
update(p << 1 | 1, l, r, add, mul);
push_up(p);
}
}
ll query(int p, int l, int r)
{
if (l <= tree[p].l && tree[p].r <= r)
return tree[p].sum;
spread(p);
ll ans = 0;
int mid = tree[p].l + tree[p].r >> 1;
if (l <= mid)
ans = (ans + query(p << 1, l, r)) % M;
if (r > mid)
ans = (ans + query(p << 1 | 1, l, r)) % M;
return ans;
}
int main()
{
int n, q, l, r, val, opt;
scanf("%d%d%d", &n, &q, &M);
for (int i = 1; i <= n; ++i)
scanf("%lld", a + i);
build(1, 1, n);
while (q--) {
scanf("%d", &opt);
if (opt == 1) {
scanf("%d%d%d", &l, &r, &val);
update(1, l, r, 0, val);
} else if (opt == 2) {
scanf("%d%d%d", &l, &r, &val);
update(1, l, r, val, 1);
} else {
scanf("%d %d", &l, &r);
printf("%lld\n", query(1, l, r));
}
}
return 0;
}
P4588 [TJOI2018]数学计算
如果直接模拟的话,因为 mod 不一定是质数,而欧拉函数的复杂度是 O ( n l o g n ) O(\sqrt nlogn) O(nlogn),计算 q 次会超时。
可以用线段树来维护乘积,a 数组来记录每次操作的乘数,先将 a 数组初始化为 1,再从 1 到 q 进行建树。如果进行 1 操作,就将 1 更新为新的值。如果进行 2 操作,就将之前的位置重置为 1 。
每次输出query(1, 1, i)(也就是tree[1].sum)
因为建树是从1到q,一开始写的1到n,一直RE
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
const int N = 1e5 + 10;
typedef long long ll;
ll a[N];
int M;
struct Node {
int l, r;
ll sum, maxv, lazy;
void updade(ll x) {
sum = x;
//maxv += x;
lazy = x;
}
} tree[N * 4];
void build(int p, int l, int r)
{
tree[p].l = l, tree[p].r = r;
tree[p].sum = 1, tree[p].lazy = 0;
if (l == r) {
tree[p].sum = tree[p].maxv = a[l];
return;
}
int mid = l + r >> 1;
build(p << 1, l , mid);
build(p << 1 | 1, mid + 1, r);
tree[p].sum = (tree[p << 1].sum * tree[p << 1 | 1].sum) % M;
//tree[p].maxv = max(tree[p << 1].maxv, tree[p << 1 | 1].maxv);
}
void spread(int p)
{
int lazy_val = tree[p].lazy;
if (lazy_val) {
tree[p << 1].updade(lazy_val);
tree[p << 1 | 1].updade(lazy_val);
tree[p].lazy = 0;
}
}
void update(int p, int l, int r, int val)
{
if (l <= tree[p].l && tree[p].r <= r) {
tree[p].updade(val);
} else {
//spread(p);
int mid = tree[p].l + tree[p].r >> 1;
if (l <= mid)
update(p << 1, l, r, val);
if (r > mid)
update(p << 1 | 1, l, r, val);
tree[p].sum = (tree[p << 1].sum * tree[p << 1 | 1].sum) % M;
//tree[p].maxv = max(tree[p << 1].maxv , tree[p << 1 | 1].maxv);
}
}
ll query(int p, int l, int r)
{
if (l <= tree[p].l && tree[p].r <= r)
return tree[p].sum;
//spread(p);
ll ans = 1;
int mid = tree[p].l + tree[p].r >> 1;
if (l <= mid)
ans = (ans * query(p << 1, l, r)) % M;
if (r > mid)
ans = (ans * query(p << 1 | 1, l, r)) % M;
return ans;
}
int main()
{
int t, n, q, x, val, opt;
scanf("%d", &t);
while (t--) {
scanf("%d%d", &q, &M);
for (int i = 1; i <= q; i++)
a[i] = 1;
build(1, 1, q);
for (int i = 1; i <= q; i++) {
scanf("%d%d", &opt, &x);
if (opt == 1) {
update(1, i, i, x);
} else {
update(1, x, x, 1);
}
printf("%lld\n", query(1, 1, q));
}
}
return 0;
}
P2574 XOR的艺术
因为对同一个区间异或两次,相当于没有异或,所以可以用懒标来记录异或的次数,最后对次数%2即可。
注意update的ans初始值
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
const int N = 2e5 + 10;
typedef long long ll;
ll a[N];
struct Node {
int l, r;
ll sum, maxv, lazy;
void updade() {
sum = (r - l + 1) - sum;
//maxv += x;
lazy += 1;
}
} tree[N * 4];
void build(int p, int l, int r)
{
tree[p].l = l, tree[p].r = r;
tree[p].sum = tree[p].lazy = 0;
if (l == r) {
tree[p].sum = tree[p].maxv = a[l];
return;
}
int mid = l + r >> 1;
build(p << 1, l , mid);
build(p << 1 | 1, mid + 1, r);
tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum;
//tree[p].maxv = max(tree[p << 1].maxv, tree[p << 1 | 1].maxv);
}
void spread(int p)
{
int lazy_val = tree[p].lazy % 2;
if (lazy_val) {
tree[p << 1].updade();
tree[p << 1 | 1].updade();
tree[p].lazy = 0;
}
}
void update(int p, int l, int r)
{
if (l <= tree[p].l && tree[p].r <= r) {
tree[p].updade();
} else {
spread(p);
int mid = tree[p].l + tree[p].r >> 1;
if (l <= mid)
update(p << 1, l, r);
if (r > mid)
update(p << 1 | 1, l, r);
tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum;
//tree[p].maxv = max(tree[p << 1].maxv , tree[p << 1 | 1].maxv);
}
}
ll query(int p, int l, int r)
{
if (l <= tree[p].l && tree[p].r <= r)
return tree[p].sum;
spread(p);
ll ans = 0;
int mid = tree[p].l + tree[p].r >> 1;
if (l <= mid)
ans = ans + query(p << 1, l, r);
if (r > mid)
ans = ans + query(p << 1 | 1, l, r);
return ans;
}
int main()
{
int n, q, l, r, val, opt;
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; ++i) {
scanf("%1lld", a + i);
}
build(1, 1, n);
while (q--) {
scanf("%d", &opt);
if (opt == 1) {
scanf("%d %d", &l, &r);
printf("%lld\n", query(1, l, r));
} else {
scanf("%d %d", &l, &r);
update(1, l, r);
}
}
return 0;
}
P3374 【模板】树状数组 1
单点修改和区间查询
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long ll;
int n, m, a[N]; // a 为原数组
ll c[N]; // c 为树状数组
// 返回x的二进制数下最小2的次幂
int lowbit(int x)
{
return x & -x;
}
// 查询前缀和
ll ask(int x)
{
ll ans = 0;
for (; x; x -= lowbit(x))
ans += c[x];
return ans;
}
// 单点增加
void add(int x, int y)
{
for (; x <= n; x += lowbit(x))
c[x] += y;
}
int main(void)
{
int op, x, y;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
add(i, a[i]);
}
while (m--) {
scanf("%d%d%d", &op, &x, &y);
if (op == 1) {
add(x, y);
} else {
ll res = ask(y) - ask(x - 1);
printf("%lld\n", res);
}
}
return 0;
}
P3368 【模板】树状数组 2
因为树状数组只能进行复杂度为 O(logn) 的单点修改,所以如果暴力进行区间修改的话,复杂度是 O(nlogn),会超时。
可以用树状数组 c 来记录每次区间修改的影响(即差分的思想),当对 [x, y] 进行加 k 时,就进行 add(x, k) 和 add(y+1, -k)。
又因为数组数组能够求前缀和,所以此时 ask(x) 即为修改操作对 a[x] 造成的影响。
所以修改后的值即为 a[x] 加上对 a[x] 造成的影响,即:a[x] + ask(x)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long ll;
int n, m, a[N]; // a 为原数组
ll c[N]; // c 为树状数组
// 返回x的二进制数下最小2的次幂
int lowbit(int x)
{
return x & -x;
}
// 查询前缀和
ll ask(int x)
{
ll ans = 0;
for (; x; x -= lowbit(x))
ans += c[x];
return ans;
}
// 单点增加
void add(int x, int y)
{
for (; x <= n; x += lowbit(x))
c[x] += y;
}
int main(void)
{
int op, x, y, k;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
while (m--) {
scanf("%d", &op);
if (op == 1) {
scanf("%d%d%d", &x, &y, &k);
add(x, k); add(y + 1, -k);
} else {
scanf("%d", &x);
ll res = a[x] + ask(x);
printf("%lld\n", res);
}
}
return 0;
}
P1908 逆序对
树状数组 c 中记录每个数字出现的次数,因为数字比较大,要进行离散化
一开始离散化出了问题,用map进行离散化会超时,需要自己手动实现离散化
从右往左依次进行遍历,查询当前数大于多少个它右边的数字,并加到res中
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long ll;
int n, m, a[N], b[N]; // a 为原数组
ll c[N]; // c 为树状数组
// 返回x的二进制数下最小2的次幂
int lowbit(int x)
{
return x & -x;
}
// 查询前缀和
ll ask(int x)
{
ll ans = 0;
for (; x; x -= lowbit(x))
ans += c[x];
return ans;
}
// 单点增加
void add(int x, int y)
{
for (; x <= n; x += lowbit(x))
c[x] += y;
}
void discrete()
{
sort(b + 1, b + n + 1);
for (int i = 1; i <= n; i++) {
if (i == 1 || b[i] != b[i - 1])
b[++m] = b[i];
}
}
int main(void)
{
int op, x, y;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
b[i] = a[i];
}
discrete();
int idx;
ll res = 0;
for (int i = n; i >= 1; i--) {
// 离散化后的数字
idx = lower_bound(b + 1, b + m + 1, a[i]) - b;
res += ask(idx - 1);
add(idx, 1);
}
printf("%lld\n", res);
return 0;
}