大意:有$n$个格子, 初始$i$位置的颜色为$i$, 美丽值为0, 有两种操作
- 将区间$[l,r]$内的元素全部改为$x$, 每个元素的美丽值增加$|x-y|$, $y$为未改动时的值
- 询问区间$[l,r]$所有元素的美丽值之和
现在给定$m$个操作, 让你输出所有操作2的询问结果.
直接线段树暴力修改, 操作2复杂度显然$O(logn)$, 考虑操作1复杂度的证明.
操作1可以看成先区间增加贡献, 之后再区间赋值, 会产生额外复杂度的只有杂色区间, 考虑杂色区间的势能.
将初值看做n次赋值操作, 不影响复杂度的证明, 现在初始是纯色的, 势能为0.
考虑$[l,r]$范围内的一次操作1, 假设$[l,r]$内杂色区间数为H.
对于区间增加贡献, 复杂度为$O(logn+H)$, 不改变势能.
对于区间赋值, 复杂度$O(logn)$, 势能会减少H, 并且最多会再增加$O(logn)$新的势能, 因为覆盖到的区间数是$O(logn)$的.
所以总复杂度$(mlogn)$.
#include <iostream> #include <algorithm> #include <cstdio> #define REP(i,a,n) for(int i=a;i<=n;++i) #define mid ((l+r)>>1) #define lc (o<<1) #define rc (lc|1) #define ls lc,l,mid #define rs rc,mid+1,r using namespace std; typedef long long ll; const int N = 1e5+10; int n, m; struct _ { int c; ll sum, tag; void upd(int cc, ll x, int len) { c=cc,sum+=len*x,tag+=x; } _ operator + (const _&rhs) const { _ r; r.c = (c==rhs.c?c:0); r.sum = sum+rhs.sum; r.tag = 0; return r; } } tr[N<<2]; void pd(int o, int l, int r) { if (tr[o].tag) { tr[lc].upd(tr[o].c,tr[o].tag,mid-l+1); tr[rc].upd(tr[o].c,tr[o].tag,r-mid); tr[o].tag=0; } } void update(int o, int l, int r, int ql, int qr, int x) { if (ql<=l&&r<=qr&&tr[o].c) return tr[o].upd(x,abs(x-tr[o].c),r-l+1); pd(o,l,r); if (mid>=ql) update(ls,ql,qr,x); if (mid<qr) update(rs,ql,qr,x); tr[o] = tr[lc]+tr[rc]; } ll query(int o, int l, int r, int ql, int qr) { if (ql<=l&&r<=qr) return tr[o].sum; pd(o,l,r); ll ans = 0; if (mid>=ql) ans+=query(ls,ql,qr); if (mid<qr) ans+=query(rs,ql,qr); return ans; } void build(int o, int l, int r) { if (l==r) return tr[o].c=l,void(); build(ls),build(rs); } int main() { scanf("%d%d", &n, &m); build(1,1,n); REP(i,1,m) { int op, l, r, x; scanf("%d%d%d", &op, &l, &r); if (op==1) { scanf("%d", &x); update(1,1,n,l,r,x); } else { printf("%lld\n", query(1,1,n,l,r)); } } }