2023“钉耙编程”中国大学生算法设计超级联赛(1)

博客围绕hdu7284题展开,该题给出长度为n的数组及两种操作。解题使用线段树,将绝对值打开分两种情况处理,利用xj≤xj+1性质,判断数的状态转变。最终创建两个线段树分别负责不同情况,最后将结果相加。

“钉耙编程"(1)1010 | hdu7284:Easy problem I

给出长度为 n n n的数组 a [ i ] a[i] a[i],对数组有两种操作:

  1. 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. 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 0xjxj+1107,并且 n , m ≤ 2 × 1 0 5 n, m\leq 2\times 10^5 n,m2×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] xa[i],即令 a [ i ] a[i] a[i]取负,然后加上x,也可以用线段树维护。

抓住 x j ≤ x j + 1 x_j \le x_{j+1} xjxj+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();   
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值