P3373 线段树模板 2

\(1 \leq n \leq 10^5\)

题解:考查标记与标记的合并

  • 我们考虑打两个懒惰标记实现区间乘和区间加
  • 线段树维护区间和
  • 对于信息与信息的合并:左儿子加上右儿子即可
  • 对于标记与标记的合并:
  • 我们发现如果某个区间存在加法的\(lazy\),但是现在对该区间又打上乘法的\(lazy\),如果我们不更新加法的\(lazy\),那么下放加法\(lazy\)的时候会出现错误
  • 所以我们在打乘法\(lazy\)的时候也要将加法的\(lazy\)乘上相应的标记值,这是两个不同的标记之间的合并
  • 对于相同标记之间,直接合并即可
  • 对于信息和标记的合并,简单合并即可
  • 标记下放时我们注意,因为加法的\(lazy\)有可能被更新过了,所以我们应该先下放乘法标记并更新区间信息,再下放加法标记并更新区间信息即可
const int N = 1e5 + 10, M = 4e5 + 10;

int n, q, m;
int a[N];
struct info
{
int sum;
};
struct SEG
{
int mul_lazy, add_lazy;
int len;
info val;
SEG()
{
mul_lazy = 1;
add_lazy = 0;
}
} seg[N << 2];
info operator+(const info &a, const info &b)
{
info c;
c.sum = (a.sum + b.sum) % m;
return c;
} void settag_mul(int id, int tag)
{
seg[id].val.sum = seg[id].val.sum * tag % m;
seg[id].mul_lazy = seg[id].mul_lazy * tag % m;
seg[id].add_lazy = seg[id].add_lazy * tag % m;
} void settag_add(int id, int tag)
{
seg[id].val.sum = (seg[id].val.sum + seg[id].len * tag % m) % m;
seg[id].add_lazy = (seg[id].add_lazy + tag) % m;
} void up(int id)
{
seg[id].val = seg[lson].val + seg[rson].val;
} void down(int id)
{
if (seg[id].mul_lazy != 1)
{
settag_mul(lson, seg[id].mul_lazy);
settag_mul(rson, seg[id].mul_lazy);
}
if (seg[id].add_lazy != 0)
{
settag_add(lson, seg[id].add_lazy);
settag_add(rson, seg[id].add_lazy);
}
seg[id].add_lazy = 0, seg[id].mul_lazy = 1;
} void build(int id, int l, int r)
{
seg[id].len = r - l + 1;
if (l == r)
{
seg[id].val.sum = a[l];
return;
}
int mid = l + r >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
up(id);
} void modify(int id, int l, int r, int ql, int qr, int op, int val)
{
if (ql <= l && r <= qr)
{
if (op == 1)
settag_mul(id, val);
else
settag_add(id, val);
return;
}
down(id);
int mid = l + r >> 1;
if (qr <= mid)
modify(lson, l, mid, ql, qr, op, val);
else if (ql > mid)
modify(rson, mid + 1, r, ql, qr, op, val);
else
{
modify(lson, l, mid, ql, qr, op, val);
modify(rson, mid + 1, r, ql, qr, op, val);
}
up(id);
} info query(int id, int l, int r, int ql, int qr)
{
if (ql <= l && r <= qr)
{
return seg[id].val;
}
down(id);
int mid = l + r >> 1;
if (qr <= mid)
return query(lson, l, mid, ql, qr);
else if (ql > mid)
return query(rson, mid + 1, r, ql, qr);
else
return query(lson, l, mid, ql, qr) + query(rson, mid + 1, r, ql, qr);
} void solve()
{
cin >> n >> q >> m;
for (int i = 1; i <= n; ++i)
cin >> a[i];
build(1, 1, n);
while (q--)
{
int op, l, r, x;
cin >> op >> l >> r;
if (op == 1)
{
cin >> x;
modify(1, 1, n, l, r, 1, x);
}
else if (op == 2)
{
cin >> x;
modify(1, 1, n, l, r, 2, x);
}
else
{
cout << query(1, 1, n, l, r).sum % m << endl;
}
}
}

P1276 校门外的树(增强版)

\(1 \leq L \leq 10^4\)

题解:两颗线段树

  • 由于懒惰标记较为麻烦,我们考虑建两颗线段树
  • 一颗维护树和树苗存在的数量,另一颗维护树存在的数量
  • 那么对于答案我们可以通过简单容斥获得
  • 最后留下的树苗 = 最后留下的树和树苗的数量 - 最后留下的树的数量
  • 每次被砍掉的树苗 = 每次砍掉的树和树苗的数量 - 每次砍掉的树的数量,统计每次数量即可
  • 我们考虑线段树如何维护数量
  • 线段树维护区间和
  • 一开始所有叶子节点全为\(1\)
  • 每次砍树,我们将对应的区间的区间和赋值为\(0\),并利用懒惰标记更新下面的区间
  • 每次种树,我们将对应的区间的区间和赋值为区间长度,并利用懒惰标记更新下面的区间
const int N = 1e4 + 10, M = 4e5 + 10;

int n, q;
struct SEG_TREE
{
struct info
{
int sum;
// 信息与信息的合并
friend info operator+(const info &a, const info &b)
{
info c;
c.sum = a.sum + b.sum;
return c;
}
};
struct SEG
{
int lazy, len;
info val;
} seg[N << 2];
void settag(int id, int tag)
{
// 信息与标记的合并
if (tag == -1) // 砍树,区间赋0
seg[id].val.sum = 0;
else if (tag == 1) // 种树, 区间赋1
seg[id].val.sum = seg[id].len;
// 标记与标记的合并
seg[id].lazy = tag;
}
void up(int id)
{
seg[id].val = seg[lson].val + seg[rson].val;
}
void down(int id)
{
if (seg[id].lazy == 0)
return;
// 下放标记
settag(lson, seg[id].lazy);
settag(rson, seg[id].lazy);
// 清空标记
seg[id].lazy = 0;
}
void build(int id, int l, int r)
{
seg[id].len = r - l + 1;
if (l == r)
{
seg[id].val.sum = 1;
return;
}
int mid = l + r >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
up(id);
}
void modify(int id, int l, int r, int ql, int qr, int val)
{
if (ql <= l && r <= qr)
{
settag(id, val);
return;
}
down(id);
int mid = l + r >> 1;
if (qr <= mid)
modify(lson, l, mid, ql, qr, val);
else if (ql > mid)
modify(rson, mid + 1, r, ql, qr, val);
else
{
modify(lson, l, mid, ql, qr, val);
modify(rson, mid + 1, r, ql, qr, val);
}
up(id);
}
info query(int id, int l, int r, int ql, int qr)
{
if (ql <= l && r <= qr)
{
return seg[id].val;
}
down(id);
int mid = l + r >> 1;
if (qr <= mid)
return query(lson, l, mid, ql, qr);
else if (ql > mid)
return query(rson, mid + 1, r, ql, qr);
else
return query(lson, l, mid, ql, qr) + query(rson, mid + 1, r, ql, qr);
}
} tr1, tr2; void solve()
{
cin >> n >> q;
++n;
tr1.build(1, 1, n), tr2.build(1, 1, n);
int ans = 0;
while (q--)
{
int op, l, r;
cin >> op >> l >> r;
++l, ++r;
if (op == 0)
{
// 被砍掉的树苗和树的总数, 被砍掉的树的总数
int sum1 = tr1.query(1, 1, n, 1, n).sum, sum2 = tr2.query(1, 1, n, 1, n).sum;
tr1.modify(1, 1, n, l, r, -1);
tr2.modify(1, 1, n, l, r, -1);
sum1 -= tr1.query(1, 1, n, 1, n).sum;
sum2 -= tr2.query(1, 1, n, 1, n).sum;
ans += sum1 - sum2;
}
else
{
tr1.modify(1, 1, n, l, r, 1);
}
}
cout << tr1.query(1, 1, n, 1, n).sum - tr2.query(1, 1, n, 1, n).sum << endl;
cout << ans << endl;
}

P5057 [CQOI2006] 简单题

\(1 \leq n \leq 10^5\)

题解:只有标记的线段树

  • 线段树不需要维护任何信息,只需要利用标记来完成数字反转
  • 对于\(lazy\)来说,\(1\)代表该区间需要反转,\(0\)代表不需要反转
  • 因为只有标记,所以我们只需要关注标记与标记的合并:
  • 如果某个区间上存在标记,说明该区间需要反转,如果我们再对该区间打上标记的话,该区间不反转,所以标记的合并实际上就是异或
  • 对于单点查询来说,如果该区间的\(lazy = 1\),直接输出\(1\)即可,否则输出\(0\)
const int N = 1e5 + 10, M = 4e5 + 10;

int n, q;
struct SEG
{
int lazy;
} seg[N << 2]; void settag(int id, int tag)
{
seg[id].lazy ^= tag;
} void down(int id)
{
if (seg[id].lazy == 0)
return;
int tag = seg[id].lazy;
settag(lson, tag);
settag(rson, tag);
seg[id].lazy = 0;
} void modify(int id, int l, int r, int ql, int qr, int tag)
{
if (ql <= l && r <= qr)
{
settag(id, tag);
return;
}
down(id);
int mid = l + r >> 1;
if (qr <= mid)
modify(lson, l, mid, ql, qr, tag);
else if (ql > mid)
modify(rson, mid + 1, r, ql, qr, tag);
else
{
modify(lson, l, mid, ql, qr, tag);
modify(rson, mid + 1, r, ql, qr, tag);
}
} int query(int id, int l, int r, int x)
{
if (l == r)
return seg[id].lazy;
down(id);
int mid = l + r >> 1;
if (x <= mid)
return query(lson, l, mid, x);
else
return query(rson, mid + 1, r, x);
} void solve()
{
cin >> n >> q;
while (q--)
{
int op, l, r, x;
cin >> op;
if (op == 1)
{
cin >> l >> r;
modify(1, 1, n, l, r, 1);
}
else
{
cin >> x;
if (query(1, 1, n, x) == 1)
cout << 1 << endl;
else
cout << 0 << endl;
}
}
}

P4588 [TJOI2018] 数学计算

\(1 \leq Q \leq 10^5\)

题解:对时间点建线段树

  • 我们考虑对于时间点建线段树
  • 线段树维护区间乘,初始所有区间为\(1\)
  • 对于操作一,我们直接在当前的时间点上修改即可
  • 对于操作二,我们直接在线段树将第\(pos\)次时间点上的修改为\(1\)即可
  • 对于查询来说,我们直接输出\(seg[1]\)即可
const int N = 1e5 + 10, M = 4e5 + 10;

int q, m;
int mp[N]; struct info
{
int ans;
};
struct SEG
{
info val;
} seg[N << 2];
info operator+(const info &a, const info &b)
{
info c;
c.ans = a.ans * b.ans % m;
return c;
} void up(int id)
{
seg[id].val = seg[lson].val + seg[rson].val;
} void build(int id, int l, int r)
{
if (l == r)
{
seg[id].val.ans = 1;
return;
}
int mid = l + r >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
up(id);
} void change(int id, int l, int r, int x, int val)
{
if (l == r)
{
seg[id].val.ans = val;
return;
}
int mid = l + r >> 1;
if (x <= mid)
change(lson, l, mid, x, val);
else
change(rson, mid + 1, r, x, val);
up(id);
} void solve()
{
cin >> q >> m;
build(1, 1, q);
for (int i = 1; i <= q; ++i)
{
int op, x;
cin >> op >> x;
if (op == 1)
{
change(1, 1, q, i, x);
cout << seg[1].val.ans % m << endl;
}
else
{
change(1, 1, q, x, 1);
cout << seg[1].val.ans % m << endl;
}
}
}

P1908 逆序对

\(1 \leq n \leq 5\times10^5\)

题解:动态开点

const int N = 5e5 + 10, M = 8e6 + 10;

int n, m;
int a[N];
int rt, lson[M], rson[M], sum[M], idx; void up(int id)
{
sum[id] = sum[lson[id]] + sum[rson[id]];
} void change(int &id, int l, int r, int x, int val)
{
if (!id)
id = ++idx;
if (l == r)
{
sum[id] += val;
return;
}
int mid = l + r >> 1;
if (x <= mid)
change(lson[id], l, mid, x, val);
else
change(rson[id], mid + 1, r, x, val);
up(id);
} int query(int id, int l, int r, int ql, int qr)
{
if (!id)
return 0;
if (ql <= l && r <= qr)
{
return sum[id];
}
int mid = l + r >> 1;
if (qr <= mid)
return query(lson[id], l, mid, ql, qr);
else if (ql > mid)
return query(rson[id], mid + 1, r, ql, qr);
else
return query(lson[id], l, mid, ql, qr) + query(rson[id], mid + 1, r, ql, qr);
} void solve()
{
m = 1e9 + 10;
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> a[i];
long long ans = 0;
for (int i = 1; i <= n; ++i)
{
ans += query(rt, 1, m, a[i] + 1, m);
change(rt, 1, m, a[i], 1);
}
cout << ans << endl;
}

Limit线段树题单题解(更新中)的更多相关文章

  1. Codeforces295A - Greg and Array(线段树的成段更新)

    题目大意 给定一个序列a[1],a[2]--a[n] 接下来给出m种操作,每种操作是以下形式的: l r d 表示把区间[l,r]内的每一个数都加上一个值d 之后有k个操作,每个操作是以下形式的: x ...

  2. HDU 1754 I Hate It(线段树区间查询,单点更新)

    描述 很多学校流行一种比较的习惯.老师们很喜欢询问,从某某到某某当中,分数最高的是多少. 这让很多学生很反感.不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问.当然,老 ...

  3. 知道创宇爬虫题--代码持续更新中 - littlethunder的专栏 - 博客频道 - CSDN.NET

    知道创宇爬虫题--代码持续更新中 - littlethunder的专栏 - 博客频道 - CSDN.NET undefined 公司介绍 - 数人科技 undefined

  4. POJ3648 A Simple Problem with Integers(线段树之成段更新。入门题)

    A Simple Problem with Integers Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 53169 Acc ...

  5. hdu1166 敌兵布阵(线段树 求区间和 更新点)

    敌兵布阵 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submi ...

  6. POJ2828 Buy Tickets 【线段树】+【单点更新】+【逆序】

    Buy Tickets Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 12296   Accepted: 6071 Desc ...

  7. hdu 1754 I Hate It (线段树功能:单点更新和区间最值)

    版权声明:本文为博主原创文章.未经博主同意不得转载.vasttian https://blog.csdn.net/u012860063/article/details/32982923 转载请注明出处 ...

  8. HDU 2795 线段树区间最大值,单点更新+二分

    Billboard Time Limit: 20000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  9. 线段树之成段更新( 需要用到延迟标记,简单来说就是每次更新的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新or询问到的时候)

    HDU  1698 链接:  http://acm.hdu.edu.cn/showproblem.php?pid=1698 线段树功能:update:成段替换 (由于只query一次总区间,所以可以直 ...

  10. XOR on segment(线段树区间异或更新)

    原题传送门 本题大意:给定n个数字和m个操作,操作共有两种,第一种是求解区间l到r上元素的和,第二种是将区间l到r的元素都异或一个x,作为某个位置的新值. 很容易想到线段树维护区间和,但是我们发现,在 ...

随机推荐

  1. RxJS 系列 – Join Creation Operators

    前言 我们一样从简单和常用的入手. 第一篇介绍了 Creation Operators 上一篇介绍了 Filter Operators 这一篇来到 Join Creation Operators. 参 ...

  2. C#|.net core 基础 - 值传递 vs 引用传递

    不知道你在开发过程中有没有遇到过这样的困惑:这个变量怎么值被改?这个值怎么没变? 今天就来和大家分享可能导致这个问题的根本原因值传递 vs 引用传递. 在此之前我们先回顾两组基本概念: 值类型 vs ...

  3. 算法学习-CDQ分治

    对于二维偏序,为普通的求逆序对,只需要先排序一遍,然后树状数组或双指针即可 而三位偏序甚至更高,则需要用 CDQ 分治,简单来说,就是将树状数组和双指针结合 操作步骤如下: 1.开始将数组按第一维排序 ...

  4. 为什么我觉得需要熟悉vim使用,难道仅仅是为了耍酷?

    实例说话: 使用vscode保存,有报提示信息,可以以超级用户身份重试,于是我授权root给vscode软件,却还提示失败! 而实际上,我使用cat命令发现已经写入成功了 终端内使用cat这条shel ...

  5. 深入C++引用及其注意事项、对引用取地址时的内存模型、const数组等

    const int f[10] = { 1,2,3,4,5,6,7,8,9,10 }; int main() { // test1 const int i = 3; int& j = cons ...

  6. 【赵渝强老师】Oracle存储过程中的out参数

    一.什么是存储过程 Oracle存储过程可以说是一个记录集吧,它是由一些PL/SQL语句组成的代码块,这些PL/SQL语句代码像一个方法一样实现一些功能(对单表或多表的增删改查),然后再给这个代码块取 ...

  7. mysql用户相关操作(转载)

    mysql用户相关操作 一. 创建用户 命令: CREATE USER 'username'@'host' IDENTIFIED BY 'password'; 说明: username:你将创建的用户 ...

  8. .NET 白板书写预测-曲线拟合

    白板软件书写速度是其最核心的功能,注册StylusPlugin从触摸线程拿触摸点数据并在另一UI线程绘制渲染是比较稳妥的方案,具体的可以查看小伙伴德熙的2019-1-28-WPF-高性能笔 - lin ...

  9. 数组对象删除不满足某些条件的对象 js

    recursiveFunction(items, childrenNodeName, ids) { console.log('items', ids); // 获取数组长度 if (items) it ...

  10. Machine Learning Week_1 Model and Cost Function 5-8

    目录 2.5 Video: Cost Function Intuition-1 unfamiliar words 2.6 Reading: Cost Function Intuition-1 unfa ...