P3722 [AH2017/HNOI2017] 影魔

P3722 [AH2017/HNOI2017] 影魔

题目背景

影魔,奈文摩尔,据说有着一个诗人的灵魂。事实上,他吞噬的诗人灵魂早已成千上万。

千百年来,他收集了各式各样的灵魂,包括诗人、牧师、帝王、乞丐、奴隶、罪人,当然,还有英雄。

每一个灵魂,都有着自己的战斗力,而影魔,靠这些战斗力提升自己的攻击。

题目描述

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

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

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

提示

对于 \(100\%\) 的数据,\(1\le n,m\le 200000, 1\le p_1, p_2\le 1000\)

Solution:

题意简述:给定一个排列 \({k_n}\) ,对于 \(i<c \le j\) 的三元组有以下两类贡献:

\(p_1\): \(k_i,k_j\) 有一个是区间最大值,另一个是次大值

\(p_2\): \(min(k_i,k_j)<k_c<max(k_i,k_j)\)

我们考虑对一个点 \(pos\) 分别求出左右两边第一个比它大的 \(k\) 的下标 \(L,R\) 可以用单调栈在 \(O(n)\) 解决

然后我们考虑如何刻画这个三元组的贡献:

\(k_L,k_R\)\(k_i,k_{i+1}\) 显然分别对区间 \([L,R]\)\([i,i+1]\)\(p_1\) 的贡献

\(L,R,pos\) 显然满足一个东西

$\forall i\in[L+1,pos-1] k_R>k_{pos}>k_i $

\(\forall i\in[pos+1,R-1] k_L>k_{pos}>k_i\)

所以:

\(\forall i\in[pos+1,R-1]\) 会在 \([L,pos]\) 上产生 \(p_2\) 的贡献

\(\forall i\in[L+1,pos-1]\) 会在 \([pos,R]\) 上产生 \(p_2\) 的贡献

这可太线段树了

我们开一颗主席树,以每个区间的左端点作为 \(rt\) 然后线段树上的下标对应右端点的区间,然后我们的贡献就可以这样维护:

if(i<n)E[i].emplace_back(i+1,i+1,p1);
if((1<=l)&&(r<=n))E[l].emplace_back(r,r,p1);
if((1<=l)&&(i+1<=r-1))E[l].emplace_back(i+1,r-1,p2);
if((l+1<=i-1)&&(r<=n))E[r].emplace_back(l+1,i-1,p2);

然后每次查询就是

ans=T.query(T.rt[l-1],T.rt[r],1,n,l,r);

然后注意一个东西,由于我的主席树的下标是挂在询问上的,所以最好写标记永久化,不然不好写 \(pushdown\)

Code

#include<bits/stdc++.h>
#define int long long
const int N=2e5+5;
using namespace std;
//Segment_Tree
struct Segment_Tree{
    int cnt;
    int rt[N];
    struct Tree{
        int ls,rs,val,tag;
    }t[N*64];
    void insert(int &x,int y,int l,int r,int L,int R,int k)
    {
        t[x=++cnt]=t[y];
        int len=(-max(L,l)+min(r,R)+1);
        t[x].val+=len*k;
        if(L<=l&&r<=R)
        {
            t[x].tag+=k;
            return ;
        }
        int mid=l+r>>1;
        if(L<=mid)insert(t[x].ls,t[y].ls,l,mid,L,R,k);
        if(mid<R) insert(t[x].rs,t[y].rs,mid+1,r,L,R,k);
    }
    int query(int x,int y,int l,int r,int L,int R)
    {
        if(!y)return 0;
        int len=(-max(L,l)+min(r,R)+1);
        if(L<=l&&r<=R)
        {
            return (-t[x].val+t[y].val);
        }
        int mid=l+r>>1,res=0;
        if(L<=mid)res+=query(t[x].ls,t[y].ls,l,mid,L,R);
        if(mid<R) res+=query(t[x].rs,t[y].rs,mid+1,r,L,R);
        res+=len*(-t[x].tag+t[y].tag);
        return res;
    }
}T;
int n,m,p1,p2;
int a[N],st[N],L[N],R[N];
vector<tuple<int,int,int> >E[N];
void work()
{
    cin>>n>>m>>p1>>p2;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        while(st[0]&&a[st[st[0]]]<a[i]){R[st[st[0]]]=i;st[0]--;}
        L[i]=st[st[0]];
        st[++st[0]]=i;
    }
    while(st[0]){R[st[st[0]]]=n+1;st[0]--;}
    for(int i=1;i<=n;i++)
    {
        int l=L[i],r=R[i];
        if(i<n)E[i].emplace_back(i+1,i+1,p1);
        if((1<=l)&&(r<=n))E[l].emplace_back(r,r,p1);
        if((1<=l)&&(i+1<=r-1))E[l].emplace_back(i+1,r-1,p2);
        if((l+1<=i-1)&&(r<=n))E[r].emplace_back(l+1,i-1,p2);
    }
    for(int i=1;i<=n;i++)
    {
        T.rt[i]=T.rt[i-1];
        for(auto [l,r,w] : E[i])
        {
            T.insert(T.rt[i],T.rt[i],1,n,l,r,w);
        }
    }
    for(int i=1,l,r;i<=m;i++)
    {
        scanf("%lld%lld",&l,&r);
        int ans=T.query(T.rt[l-1],T.rt[r],1,n,l,r);
        printf("%lld\n",ans);
    }
}
#undef int
int main()
{
    //freopen("P3722.in","r",stdin);freopen("P3722.out","w",stdout);
    work();
    return 0;
}
posted @ 2024-12-25 21:43  liuboom  阅读(23)  评论(0)    收藏  举报