介绍两种解法。(分块理论上也能做,感兴趣自行实现)
纯模板,不解释。仅适用于随机数据,非随机数据或初始不是常数列会 TLE 。复杂度 O(n\log\log n)
#include <bits/stdc++.h>
using namespace std;
#define sc(x) scanf("%lld", &x)
typedef long long ll;
ll n;
struct node_t
{
ll l, r;
mutable ll v; // mutable使得const可变(set元素是const)
node_t(const ll &il, const ll &ir, const ll &iv) : l(il), r(ir), v(iv) {}
inline bool operator<(const node_t &o) const { return l < o.l; }
};
set<node_t> odt; //建立一棵珂朵莉树
auto split(ll x) //珂朵莉基操
{
if (x > n)
return odt.end();
auto it = --odt.upper_bound(node_t{x, 0, 0});
if (it->l == x)
return it;
ll l = it->l, r = it->r, v = it->v;
odt.erase(it);
odt.insert(node_t(l, x - 1, v));
return odt.insert(node_t(x, r, v)).first;
}
void assign(ll l, ll r, ll v) //区间赋值为v, 均摊O(loglogn)
{
auto itr = split(r + 1), itl = split(l);
odt.erase(itl, itr);
odt.insert(node_t(l, r, v));
}
void add(ll l, ll r, ll v) //区间增加v, 均摊O(loglogn)
{
auto itr = split(r + 1), itl = split(l);
for (; itl != itr; ++itl) //枚举每个子区间
{
itl->v = itl->v + v;
}
}
ll query(ll l, ll r) //区间查询, 均摊O(loglogn)
{
ll res = 0;
auto itr = split(r + 1), itl = split(l);
for (; itl != itr; ++itl)
{ // itl->l, itl->r, itl->v 是当前子区间的左右端点和值
res += (itl->r - itl->l + 1) * (itl->v);
}
return res;
}
signed main()
{
sc(n);
odt.insert({1, n, 1236895}); //初始化区间
ll m, cmd, l, r, x;
for (sc(m); m--;)
{
sc(cmd), sc(l), sc(r);
if (cmd == 1)
{
sc(x), assign(l, r, x);
}
else if (cmd == 2)
{
sc(x), add(l, r, x);
}
else
{
printf("%lld\n", query(l, r));
}
}
return 0;
}
可以认为也是线段树比较经典的模板题。适用面远大于珂朵莉树。
有两个不同的区间修改操作,它们会相互影响,所以要开两个懒标记
用 laz1
代表操作 1 的懒标记(初始为 0 ,0 代表没有需要执行的懒操作),用 laz2
代表操作 2 的懒标记(初始也为 0)
对每次操作 1 ,区间赋值,把 laz1
设为 x ,把 laz2
设为 0 ,因为赋值后加法全无效了
对每次操作 2 ,区间加法,把 laz2
加上 x
根据这个想法,需要对每次 pushdown 操作,先判 laz1
,如果发现 laz1
不为 0 ,就把 laz1
下传给两儿子,并且把两儿子的 laz2
清零 (注意:这很重要)。判完之后,再判并下传 laz2
。
如果没有
laz1
下传的时候把两儿子laz2
清零,在区间赋值时,两儿子可能会多执行赋值前遗留的区间加法,从而造成结果偏大
复杂度 O((n+m)\log n)
#include <bits/stdc++.h>
using namespace std;
#define sc(x) scanf("%lld", &x)
typedef long long ll;
#define mn 100010
#define lfs p << 1
#define rfs p << 1 | 1
#define mkcf ll cf = (lf + rf) >> 1
// laz1: lazy-assign ; laz2: lazy-add
ll t[mn << 2], laz1[mn << 2], laz2[mn << 2], a[mn];
void build(ll p, ll lf, ll rf)
{
if (lf == rf)
{
t[p] = 1236895;
return;
}
mkcf;
build(lfs, lf, cf);
build(rfs, cf + 1, rf);
t[p] = t[lfs] + t[rfs];
}
void pushdown(ll p, ll lf, ll rf)
{
mkcf;
if (laz1[p])
{
t[lfs] = laz1[p] * (cf - lf + 1);
t[rfs] = laz1[p] * (rf - cf);
laz1[lfs] = laz1[rfs] = laz1[p];
laz2[lfs] = laz2[rfs] = 0; //注意要清理laz2
laz1[p] = 0;
}
t[lfs] += laz2[p] * (cf - lf + 1);
t[rfs] += laz2[p] * (rf - cf);
laz2[lfs] += laz2[p], laz2[rfs] += laz2[p];
laz2[p] = 0;
}
void assign(ll p, ll lf, ll rf, ll lc, ll rc, ll x)
{
if (lf >= lc && rf <= rc)
{
t[p] = (rf - lf + 1) * x;
laz1[p] = x;
laz2[p] = 0;
return;
}
pushdown(p, lf, rf);
mkcf;
if (cf >= lc)
{
assign(lfs, lf, cf, lc, rc, x);
}
if (cf + 1 <= rc)
{
assign(rfs, cf + 1, rf, lc, rc, x);
}
t[p] = t[lfs] + t[rfs];
}
void add(ll p, ll lf, ll rf, ll lc, ll rc, ll x)
{
if (lf >= lc && rf <= rc)
{
t[p] += (rf - lf + 1) * x;
laz2[p] += x;
return;
}
pushdown(p, lf, rf);
mkcf;
if (cf >= lc)
{
add(lfs, lf, cf, lc, rc, x);
}
if (cf + 1 <= rc)
{
add(rfs, cf + 1, rf, lc, rc, x);
}
t[p] = t[lfs] + t[rfs];
}
ll query(ll p, ll lf, ll rf, ll lc, ll rc)
{
if (lf >= lc && rf <= rc)
{
return t[p];
}
pushdown(p, lf, rf);
mkcf;
ll res = 0;
if (cf >= lc)
{
res += query(lfs, lf, cf, lc, rc);
}
if (cf + 1 <= rc)
{
res += query(rfs, cf + 1, rf, lc, rc);
}
t[p] = t[lfs] + t[rfs];
return res;
}
signed main()
{
ll n, m, l, r, x, cmd;
sc(n), sc(m);
build(1, 1, n);
while (m--)
{
sc(cmd), sc(l), sc(r);
if (cmd == 1)
{
sc(x);
assign(1, 1, n, l, r, x);
}
else if (cmd == 2)
{
sc(x);
add(1, 1, n, l, r, x);
}
else if (cmd == 3)
{
printf("%lld\n", query(1, 1, n, l, r));
}
}
return 0;
}