洛谷 P3722 [AH2017/HNOI2017]影魔

奈文摩尔有 \(n\) 个灵魂,他们在影魔宽广的体内可以排成一排,从左至右标号 \(1\)\(n\)。第 \(i\) 个灵魂的战斗力为 \(k_i\),灵魂们以点对的形式为影魔提供攻击力。对于灵魂对 \(i, j\ (i\lt j)\) 来说,若不存在 \(k_s\ (i\lt s\lt j)\) 大于 \(k_i\) 或者 \(k_j\),则会为影魔提供 \(p_1\) 的攻击力。另一种情况,令 \(c\)\(k_{i + 1}, k_{i + 2}, \cdots, k_{j -1}\) 的最大值,若 \(c\) 满足:\(k_i \lt c \lt k_j\),或者 \(k_j \lt c \lt k_i\),则会为影魔提供 \(p_2\) 的攻击力,当这样的 \(c\) 不存在时,自然不会提供这 \(p_2\) 的攻击力;其他情况的点对,均不会为影魔提供攻击力。

影魔的挚友噬魂鬼在一天造访影魔体内时被这些灵魂吸引住了,他想知道,对于任意一段区间 \([a, b]\),位于这些区间中的灵魂对会为影魔提供多少攻击力,即考虑所有满足 \(a\le i\lt j\le b\) 的灵魂对 \(i, j\) 提供的攻击力之和。

顺带一提,灵魂的战斗力组成一个 \(1\)\(n\) 的排列:\(k_1, k_1, \cdots, k_n\)

简明题意:有一个n的排列k,对于一个区间\([l,r]\),如果\(k_l\)\(k_r\)分别是这个区间的最大值和次大值,那么产生\(p_1\)的贡献,如果\(s=\max_{i=l+1}^{r-1} k_i\)满足\(k_l<s<k_r\),则会产生\(p_2\)的贡献,每次询问一个区间的答案,答案为这个区间所有子区间产生的贡献

乍一看不太能做的样子,所以我们考虑离线操作

先对每个i处理出向左第一个比i大的数的下标\(L_i\),向右第一个比i大的数的下标\(R_i\),这个可以直接用单调栈求出

然后考虑产生的贡献

  1. 区间\([L_i,R_i]\)会产生\(p_1\)的贡献
  2. 所有左端点在\([L_i+1,i-1]\),右端点是\(R_i\)的区间会产生\(p_2\)的贡献
  3. 所有左端点是\(L_i\),右端点在\([i+1,R_i-1]\)的区间会产生\(p_2\)的贡献

于是我们离线之后每次扫到\(R_i\)时就做一次贡献,把询问拆成r询问一次答案减去l-1询问一次的答案,然后就做完了

不要忘了所有的\([i,i+1]\)也会产生\(p_1\)的贡献

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#define zrt k << 1
#define yrt k << 1 | 1
const int N = 2e5;
using namespace std;
struct Q
{
    int x,id,typ,l,r;
}q[N * 2 + 5];
int n,p1,p2,m,k[N + 5],L[N + 5],R[N + 5],stk[N + 5],top,Q_cnt,m_cnt;
long long ans[N + 5];
int cmp(Q x,Q y)
{
    return x.x < y.x;
}
struct Seg
{
    long long sm[N * 4 + 5],tag[N * 4 + 5];
    void jia(int k,int l,int r,long long z)
    {
        tag[k] += z;
        sm[k] += z * (r - l + 1);
    }
    void pushdown(int k,int l,int r,int mid)
    {
        if (tag[k])
        {
            jia(zrt,l,mid,tag[k]);
            jia(yrt,mid + 1,r,tag[k]);
            tag[k] = 0;
        }
    }
    void pushup(int k)
    {
        sm[k] = sm[zrt] + sm[yrt];
    }
    void add(int k,int l,int r,int x,int y,int z)
    {
        if (x > y)
            return;
        if (l >= x && r <= y)
        {
            tag[k] += z;
            sm[k] += 1ll * z * (r - l + 1);
            return;
        }
        int mid = l + r >> 1;
        pushdown(k,l,r,mid);
        if (x <= mid)
            add(zrt,l,mid,x,y,z);
        if (y > mid)
            add(yrt,mid + 1,r,x,y,z);
        pushup(k);
    }
    long long query(int k,int l,int r,int x,int y)
    {
        if (x > y)
            return 0;
        if (l >= x && r <= y)
            return sm[k];
        int mid = l + r >> 1;
        pushdown(k,l,r,mid);
        if (x > mid)
            return query(yrt,mid + 1,r,x,y);
        else
            if (y <= mid)
                return query(zrt,l,mid,x,y);
            else
                return query(zrt,l,mid,x,y) + query(yrt,mid + 1,r,x,y);
    }
}tree;
struct modi
{
    int l,r,x,v;
}t[N * 3 + 5];
int com(modi x,modi y)
{
    return x.x < y.x;
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&p1,&p2);
    for (int i = 1;i <= n;i++)
        scanf("%d",&k[i]);
    int l,r;
    for (int i = 1;i <= m;i++)
    {
        scanf("%d%d",&l,&r);
        q[++Q_cnt].x = l - 1;
        q[Q_cnt].id = i;
        q[Q_cnt].typ = -1;
        q[Q_cnt].l = l;
        q[Q_cnt].r = r;
        q[++Q_cnt].x = r;
        q[Q_cnt].id = i;
        q[Q_cnt].typ = 1;
        q[Q_cnt].l = l;
        q[Q_cnt].r = r;
        ans[i] = (r - l) * p1;
    }
    k[n + 1] = n + 1;
    for (int i = 1;i <= n + 1;i++)
    {
        while (top && k[stk[top]] < k[i])
            R[stk[top--]] = i;
        stk[++top] = i;
    }
    top = 0;
    for (int i = n;i >= 1;i--)
    {
        while (top && k[stk[top]] < k[i])
            L[stk[top--]] = i;
        stk[++top] = i;
    }
    for (int i = 1;i <= n;i++)
    {
        if (L[i] && R[i] <= n)
            t[++m_cnt] = (modi){L[i],L[i],R[i],p1};
        if (L[i] + 1 <= i - 1 && R[i] <= n)
            t[++m_cnt] = (modi){L[i] + 1,i - 1,R[i],p2};
        if (i + 1 <= R[i] - 1 && L[i])
            t[++m_cnt] = (modi){i + 1,R[i] - 1,L[i],p2};
    }
    sort(q + 1,q + Q_cnt + 1,cmp);
    sort(t + 1,t + m_cnt + 1,com);
    int j = 1,k = 1;
    while (!q[k].x)
        k++;
    for (int i = 1;i <= n;i++)
    {
        while (t[j].x <= i && j <= m_cnt)
        {
            tree.add(1,1,n,t[j].l,t[j].r,t[j].v);
            j++;
        }
        while (q[k].x <= i && k <= Q_cnt)
        {
            ans[q[k].id] += q[k].typ * tree.query(1,1,n,q[k].l,q[k].r);
            k++;
        }
    }
    for (int i = 1;i <= m;i++)
        printf("%lld\n",ans[i]);
    return 0;
}
posted @ 2020-06-10 09:52  eee_hoho  阅读(155)  评论(0编辑  收藏  举报