[HNOI2016]序列【笛卡尔树/单调栈+扫描线】

题目链接


  对于一个数a[pos],它是位于pos位上的数的值,假设它左边第一个比他小的数的位置+1是{\color{Red} lef[pos] },假设它右边第一个不大于它的数的位置-1是{\color{Red} rig[pos]}。那么以pos这位数产生的贡献可以考虑成(pos - lef[pos] + 1) * (rig[pos] - pos + 1) * a[pos]

  如果我们将贡献作为二维平面上的话,我们可以更加清楚的看到它的变化,我定义xoy[i][j]为点i到点j的i~j区间内的最小值,于是有xoy[i][j] = 0,\ {i > j}

  于是,对于题目中的数据“5 2 4 1 3”,我们可以看成:

  不难发现,我们如果要查询(l, r)的答案,实际上就是坐标系中(l, l)(r, r)的这个二维覆盖区间,当然,这里有的人会说到可持久化四叉树(也行,写好点就是了)。

  但是我们完全可以换成一种离线操作的方法,考虑一条平行于x轴的扫描线,我们将这条扫描线向上扫描,先考虑对于a[pos]所覆盖的区间是(lef[pos], pos) \rightarrow (pos, rig[pos])都是a[pos]。如此,我们不妨看成将pos \rightarrow rig[pos]推一条直线平行于x轴,为lef[pos] \rightarrow pos这段长度为(pos - len[pos] + 1)的全覆盖为a[pos]的线段向上推。

  直接的覆盖问题,在一维上我们可以很容易的想到是用一个差分的方法来进行计算,可以是:

  这样,当它是在pos位,l < pos < r时候,我们可以用pos * val - (l - 1) * val算得l~pos位都覆盖上val时候的值。

  在我要查询ql~qr,此时有l < ql < r < qr时候,我们可以默认"> r"之后,没有权值val了,所以是0 - ((ql - 1) * val - r * val) = val * (r - ql + 1)也可以充分的发挥差分的作用。

  现在,变成了二维区间,但是其实它是以"y = x",这条曲线同时向上和向右的,所以,我们不妨做这样的一个差分。我们将这个差分放到线段树上,用区间和减去区间和的方式做差分,又因为"y = x",这条x、y同步增长的,所以我们这做的差分其实可以很好的就是用以求(lef[pos], pos) \rightarrow (pos, rig[pos])这段的部分。因为当y < x时候,存在之前说的xoy[i][j] = 0,\ {i > j}

  所以,换到二维区间上,我们总结一下我们现在的做法,做一个扫描线,它随查询的右端点向上走,表明的是现在查询到的最右方向,所以,我们需要考虑已经覆盖完全的时候,和未覆盖完全的情况。

  • 未覆盖完全

  也就是lef[pos] \rightarrow pos还可以继续向上推,这时候我们可以在pos == l的时候将a[pos]加给[lef[pos], l]这个区间的每一位,然后我们可以用当前sum * (pos - (l - 1))的方式来得到这一段区间“未覆盖完全”的值的贡献。

  • 覆盖完全

   这时候,它都被完完全全的覆盖完了,那么,按照之前写到的应当在rig[pos] + 1位置的时候,将该pos位的a[pos]贡献给取消掉了,所以要给第rig[pos]位减去a[pos] * rig[pos],这时候,如果有查询的左区间ql是小于等于rig[pos]的时候,就可以得到(pos - ql + 1) * (rig[pos] - pos + 1) * a[pos]这样的贡献。

Summary

  总结一下,就是我们用差分来解决了未覆盖完全的部分的贡献,如果当我们查询的右区间已经覆盖过了它的rig[pos],我们可以直接查询(pos - ql + 1) * (rig[pos] - pos + 1) * a[pos]这样的贡献,所以我们需要维护两棵线段树,分别处理这两部分。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
#include <unordered_map>
#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 1e5 + 7;
int N, Q, lef[maxN], rig[maxN];
ll a[maxN], ans[maxN];
struct Question
{
    int l, r, id;
    inline void In(int ith) { scanf("%d%d", &l, &r); id = ith; }
    friend bool operator < (Question e1, Question e2) { return e1.r < e2.r; }
} q[maxN];
struct node
{
    int pos; ll val;
    node(int a=0, ll b=0):pos(a), val(b) {}
} Stap[maxN];
struct BITree
{
    ll tree[maxN << 2] = {0}, lazy[maxN << 2] = {0};
    inline void pushdown(int rt, int l, int r)
    {
        if(lazy[rt])
        {
            int mid = HalF;
            tree[lsn] += lazy[rt] * (mid - l + 1);
            tree[rsn] += lazy[rt] * (r - mid);
            lazy[lsn] += lazy[rt];
            lazy[rsn] += lazy[rt];
            lazy[rt] = 0;
        }
    }
    inline void pushup(int rt) { tree[rt] = tree[lsn] + tree[rsn]; }
    void update(int rt, int l, int r, int ql, int qr, ll val)
    {
        if(ql <= l && qr >= r)
        {
            tree[rt] += val * (r - l + 1);
            lazy[rt] += val;
            return;
        }
        pushdown(myself);
        int mid = HalF;
        if(qr <= mid) update(QL, val);
        else if(ql > mid) update(QR, val);
        else { update(QL, val); update(QR, val); }
        pushup(rt);
    }
    ll query(int rt, int l, int r, int ql, int qr)
    {
        if(ql <= l && qr >= r) return tree[rt];
        pushdown(myself);
        int mid = HalF;
        if(qr <= mid) return query(QL);
        else if(ql > mid) return query(QR);
        else return query(QL) + query(QR);
    }
} t[2];
vector<int> del[maxN];
int main()
{
    scanf("%d%d", &N, &Q);
    for(int i=1; i<=N; i++) scanf("%lld", &a[i]);
    for(int i=1; i<=Q; i++) q[i].In(i);
    sort(q + 1, q + Q + 1);
    int Stop = 0;
    Stap[0].pos = 0;
    for(int i=1; i<=N; i++)
    {
        while(Stop && Stap[Stop].val >= a[i])
        {
            rig[Stap[Stop].pos] = i - 1;
            Stop--;
        }
        Stap[++ Stop] = node(i, a[i]);
    }
    while(Stop) { rig[Stap[Stop].pos] = N; Stop--; }
    for(int i=N; i>=1; i--)
    {
        while(Stop && Stap[Stop].val > a[i])
        {
            lef[Stap[Stop].pos] = i + 1;
            Stop--;
        }
        Stap[++ Stop] = node(i, a[i]);
    }
    while(Stop) { lef[Stap[Stop].pos] = 1; Stop--; }
    for(int i=1; i<=N; i++) del[rig[i] + 1].push_back(i);
    for(int i=1, Qpos = 1, len; i<=Q; i++)
    {
        while(Qpos <= q[i].r)
        {
            t[0].update(1, 1, N, lef[Qpos], Qpos, a[Qpos] * (Qpos - 1));
            t[1].update(1, 1, N, lef[Qpos], Qpos, a[Qpos]);
            len = (int)del[Qpos].size();
            for(int j=0, id; j<len; j++)
            {
                id = del[Qpos][j];
                t[0].update(1, 1, N, lef[id], id, -1LL * a[id] * (Qpos - 1));
                t[1].update(1, 1, N, lef[id], id, - a[id]);
            }
            Qpos++;
        }
        ans[q[i].id] = 1LL * q[i].r * t[1].query(1, 1, N, q[i].l, q[i].r) - t[0].query(1, 1, N, q[i].l, q[i].r);
    }
    for(int i=1; i<=Q; i++) printf("%lld\n", ans[i]);
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值