“钉耙编程"(1)1010 | hdu7284:Easy problem I
给出长度为 n n n的数组 a [ i ] a[i] a[i],对数组有两种操作:
1 l r x:区间 [ l , r ] [l, r] [l,r]将执行 a [ i ] = ∣ a [ i ] − x ∣ a[i] = \lvert a[i] - x\rvert a[i]=∣a[i]−x∣2 l r:计算 ∑ i = l r a [ i ] \sum_{i=l}^r a[i] ∑i=lra[i]保证: 0 ≤ x j ≤ x j + 1 ≤ 1 0 7 0\le x_j \leq x_{j+1} \le 10^7 0≤xj≤xj+1≤107,并且 n , m ≤ 2 × 1 0 5 n, m\leq 2\times 10^5 n,m≤2×105
这题很是一个线段树的题
先将绝对值打开,变成了两种情况, a [ i ] > x a[i]>x a[i]>x一种, a [ i ] ≤ x a[i]\le x a[i]≤x另一种。
分开考虑两者的同与异
a [ i ] > x a[i]>x a[i]>x时,结果为 a [ i ] − x a[i] - x a[i]−x,只需要执行区间减操作即可。
a [ i ] ≤ x a[i]\le x a[i]≤x时,结果为 x − a [ i ] x - a[i] x−a[i],即令 a [ i ] a[i] a[i]取负,然后加上x,也可以用线段树维护。
抓住 x j ≤ x j + 1 x_j \le x_{j+1} xj≤xj+1
这是一个很重要的性质,有了这个性质很容易推出一个数一旦从 a [ i ] > x a[i]>x a[i]>x的状态转变为 a [ i ] ≤ x a[i]\le x a[i]≤x,那么将永远无法逆转。
所以可以暴力判断每个数是否小于 x x x,一旦小于x就转移到另一个线段树上。
最终
最后的实现是创建两个线段树,线段树1号负责 a [ i ] > x a[i]>x a[i]>x,通过判断区间最小值是否小于 0 0 0,来判断有无需要转移的 x x x。线段树2号负责另外的工作。最后将二者加起来就可以。
代码写了200+行 (T _ T)
#include <bits/stdc++.h>
#define lc x << 1, l, mid
#define rc x << 1 | 1, mid + 1, r
#define rt 1, 1, n
using namespace std;
typedef long long ll;
const ll INF = 1e18;
const int N = 2e5 + 10;
int n, m;
ll a[N];
struct tree2
{
ll sum[N << 2], lazy[N << 2], neg[N << 2], valid[N << 2];
void pushdown(int x)
{
int lson = x << 1, rson = x << 1 | 1;
if(neg[x])
{
sum[lson] *= -1, sum[rson] *= -1;
lazy[lson] *= -1, lazy[rson] *= -1;
}
sum[lson] += valid[lson] * lazy[x];
sum[rson] += valid[rson] * lazy[x];
lazy[lson] += lazy[x];
lazy[rson] += lazy[x];
neg[lson] ^= neg[x];
neg[rson] ^= neg[x];
lazy[x] = neg[x] = 0;
}
void pushup(int x)
{
int lson = x << 1, rson = lson | 1;
valid[x] = valid[lson] + valid[rson];
sum[x] = sum[lson] + sum[rson];
}
void build(int x, int l, int r)
{
sum[x] = lazy[x] = neg[x] = valid[x] = 0;
if(l == r) return ;
int mid = l + r >> 1;
build(lc);
build(rc);
}
void setValue(int x, int l, int r, int p, ll val)
{
if(l == r)
{
sum[x] = val;
valid[x] = 1;
return ;
}
pushdown(x);
int mid = l + r >> 1;
if(p <= mid) setValue(lc, p, val);
else setValue(rc, p, val);
pushup(x);
}
void change(int x, int l, int r, int ql, int qr, ll val) // val - a[x]
{
if(l == ql && r == qr)
{
sum[x] = val * valid[x] - sum[x];
lazy[x] = val - lazy[x];
neg[x] ^= 1;
return ;
}
pushdown(x);
int mid = l + r >> 1;
if(qr <= mid) change(lc, ql, qr, val);
else if(mid < ql) change(rc, ql, qr, val);
else change(lc, ql, mid, val), change(rc, mid + 1, qr, val);
pushup(x);
}
ll query(int x, int l, int r, int ql, int qr)
{
if(l == ql && r == qr) return sum[x];
pushdown(x);
int mid = l + r >> 1;
if(qr <= mid) return query(lc, ql, qr);
else if(mid < ql) return query(rc, ql, qr);
else return query(lc, ql, mid) + query(rc, mid + 1, qr);
}
}tr2;
struct tr1
{
ll sum[N << 2], lazy[N << 2], mn[N << 2], valid[N << 2];
inline void pushdown(int x)
{
int lson = x << 1, rson = lson | 1;
sum[lson] += lazy[x] * valid[lson];
sum[rson] += lazy[x] * valid[rson];
lazy[lson] += lazy[x];
lazy[rson] += lazy[x];
mn[lson] += lazy[x];
mn[rson] += lazy[x];
lazy[x] = 0;
}
inline void pushup(int x)
{
int lson = x << 1, rson = x << 1 | 1;
sum[x] = sum[lson] + sum[rson];
mn[x] = min(mn[lson], mn[rson]);
valid[x] = valid[lson] + valid[rson];
}
void build(int x, int l, int r)
{
lazy[x] = 0;
if(l == r)
{
sum[x] = mn[x] = a[l];
valid[x] = 1;
return ;
}
int mid = l + r >> 1;
build(lc);
build(rc); // debug
pushup(x);
}
void reset(int x) // a[p] = 0
{
sum[x] = valid[x] = 0;
mn[x] = INF;
}
void movePoint(int x, int l, int r, ll val)
{
tr2.setValue(rt, l, -sum[x]);
reset(x);
}
void move(int x, int l, int r, ll val)
{
if(l == r)
{
movePoint(x, l, r, val);
return ;
}
pushdown(x);
int mid = l + r >> 1;
int lson = x << 1, rson = x << 1 | 1;
if(mn[lson] < 0) move(lc, val);
if(mn[rson] < 0) move(rc, val);
pushup(x);
}
void change(int x, int l, int r, int ql, int qr, ll val) // a[x] - val
{
if(l == ql && r == qr)
{
sum[x] -= valid[x] * val;
lazy[x] -= val;
mn[x] -= val;
if(mn[x] < 0) move(x, l, r, val);
return ;
}
pushdown(x);
int mid = l + r >> 1;
if(qr <= mid) change(lc, ql, qr, val);
else if(mid < ql) change(rc, ql, qr, val);
else change(lc, ql, mid, val), change(rc, mid + 1, qr, val);
pushup(x);
}
ll query(int x, int l, int r, int ql, int qr)
{
if(l == ql && r == qr) return sum[x];
pushdown(x);
int mid = l + r >> 1;
if(qr <= mid) return query(lc, ql, qr);
else if(mid < ql) return query(rc, ql, qr);
else return query(lc, ql, mid) + query(rc, mid + 1, qr); // debug l <-> ql
}
}tr1;
void solve()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)
{
scanf("%lld", &a[i]);
}
tr1.build(rt);
tr2.build(rt);
for(int i = 1; i <= m; i++)
{
int op, l, r;
ll x;
scanf("%d", &op);
if(op == 1)
{
scanf("%d%d%lld", &l, &r, &x);
tr2.change(rt, l, r, x); // NOTICE the order
tr1.change(rt, l, r, x);
}
else// op == 2
{
scanf("%d%d", &l, &r);
ll ans = tr1.query(rt, l, r) + tr2.query(rt, l, r);
printf("%lld\n", ans);
}
}
}
int main()
{
int T;
scanf("%d", &T);
while (T--) solve();
}
博客围绕hdu7284题展开,该题给出长度为n的数组及两种操作。解题使用线段树,将绝对值打开分两种情况处理,利用xj≤xj+1性质,判断数的状态转变。最终创建两个线段树分别负责不同情况,最后将结果相加。

668

被折叠的 条评论
为什么被折叠?



