BZOJ4826 [Hnoi2017]影魔 【线段树 + 单调栈】

题目链接

BZOJ4826

题解

蒟蒻智力水平捉急orz

我们会发现相邻的\(i\)和\(j\)贡献一定是\(p1\),可以很快算出来【然而我一开始忘了考虑调了半天】

我们现在只考虑不相邻的

我们只需要找出所有产生贡献的\(i,j\)即可

我们发现每一个产生贡献的\(i,j\)都能对应到一个三元组\((i,k,j)\),分别对应区间的最大值,次大值,第三大值

我们枚举中间位置\(i\),找到\(i\)左边第一个比\(i\)大的位置\(L[i]\),右边第一个比\(i\)大的位置\(R[i]\)

那么\(L[i]\)和\(R[i]\)的贡献就是\(p1\)

区间\((L[i],i)\)和\(R[i]\)的贡献是\(p2\)

区间\((i,R[i])\)和\(L[i]\)的贡献是\(p2\)

一对点对询问产生贡献,当且仅当其都在区间中

所以我们可以用一个端点去储存贡献,而另一个端点作为更新的位置

然后再离线询问,用到\(r\)端点时区间\([l,r]\)的贡献减去到\(l - 1\)时区间\([l,r]\)的贡献,就是答案

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<vector>
#include<cmath>
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define LL long long int
#define ls (u << 1)
#define rs (u << 1 | 1)
using namespace std;
const int maxn = 200005,maxm = 100005,INF = 1000000000;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    return out * flag;
}
struct node{int l,r;};
struct Que{int l,r,pos,t,id;}q[maxn << 1];
vector<node> Ln[maxn],Rn[maxn];
LL ans[maxn];
LL A[maxn],n,m,p1,p2,qi;
LL st[maxn],top,Li[maxn],Ri[maxn];
LL sum[maxn << 2],tag[maxn << 2];
inline bool operator <(const Que& a,const Que& b){
    return a.pos < b.pos;
}
void upd(int u){sum[u] = sum[ls] + sum[rs];}
void pd(int u,int l,int r){
    if (tag[u]){
        int mid = l + r >> 1;
        sum[ls] += tag[u] * (mid - l + 1);
        sum[rs] += tag[u] * (r - mid);
        tag[ls] += tag[u]; tag[rs] += tag[u];
        tag[u] = 0;
    }
}
void add(int u,int l,int r,int L,int R,LL v){
    if (l >= L && r <= R){sum[u] += v * (r - l + 1); tag[u] += v; return;}
    pd(u,l,r);
    int mid = l + r >> 1;
    if (mid >= L) add(ls,l,mid,L,R,v);
    if (mid < R) add(rs,mid + 1,r,L,R,v);
    upd(u);
}
LL query(int u,int l,int r,int L,int R){
    if (l >= L && r <= R) return sum[u];
    pd(u,l,r);
    int mid = l + r >> 1;
    if (mid >= R) return query(ls,l,mid,L,R);
    if (mid < L) return query(rs,mid + 1,r,L,R);
    return query(ls,l,mid,L,R) + query(rs,mid + 1,r,L,R);
}
void init(){
    for (int i = 1; i <= n; i++){
        while (top && A[i] > A[st[top]]) top--;
        Li[i] = st[top];
        st[++top] = i;
    }
    st[0] = n + 1; top = 0;
    for (int i = n; i; i--){
        while (top && A[i] > A[st[top]]) top--;
        Ri[i] = st[top];
        st[++top] = i;
    }
    for (int i = 1; i <= n; i++){
        //printf("pos: %d   L:[%d,%d]  R:[%d,%d]\n",i,Li[i] + 1,i - 1,i + 1,Ri[i] - 1);
        if (Li[i])
            Ln[Li[i]].push_back((node){i + 1,Ri[i] - 1});
        if (Ri[i] <= n)
            Rn[Ri[i]].push_back((node){Li[i] + 1,i - 1});
    }
}
void solve(){
    sort(q + 1,q + 1 + qi); int l,r,pos = 1;
    for (int i = 0; i <= n; i++){
        for (unsigned int j = 0; j < Ln[i].size(); j++){
            l = Ln[i][j].l; r = Ln[i][j].r;
            if (l <= r) add(1,1,n,l,r,p2);
        }
        for (unsigned int j = 0; j < Rn[i].size(); j++){
            l = Rn[i][j].l; r = Rn[i][j].r;
            if (l <= r) add(1,1,n,l,r,p2);
            if (l - 1 > 0) add(1,1,n,l - 1,l - 1,p1);
        }
        while (pos <= qi && q[pos].pos == i){
            ans[q[pos].id] += query(1,1,n,q[pos].l,q[pos].r) * q[pos].t;
            pos++;
        }
    }
    for (int i = 1; i <= m; i++)
        printf("%lld\n",ans[i]);
}
int main(){
    n = read(); m = read(); p1 = read(); p2 = read(); int l,r;
    for (int i = 1; i <= n; i++) A[i] = read();
    for (int i = 1; i <= m; i++){
        l = read(); r = read();
        q[++qi] = (Que){l,r,l - 1,-1,i};
        q[++qi] = (Que){l,r,r,1,i};
        ans[i] += 1ll * (r - l) * p1;
    }
    init();
    solve();
    return 0;
}

原文地址:https://www.cnblogs.com/Mychael/p/9042978.html

时间: 2024-11-03 01:23:09

BZOJ4826 [Hnoi2017]影魔 【线段树 + 单调栈】的相关文章

[BZOJ4826][HNOI2017]影魔(主席树)

4826: [Hnoi2017]影魔 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 669  Solved: 384[Submit][Status][Discuss] Description 影魔,奈文摩尔,据说有着一个诗人的灵魂.事实上,他吞噬的诗人灵魂早已成千上万.千百年来,他收集了各式各样 的灵魂,包括诗人.牧师.帝王.乞丐.奴隶.罪人,当然,还有英雄.每一个灵魂,都有着自己的战斗力,而影魔,靠 这些战斗力提升自己的攻击.奈文摩尔有 n 个

【CF997E】Good Subsegments (线段树+单调栈)

Description 原题链接 给你一个长度为\(n\)的排列\(~P\),定义一段子区间是好的,当且仅当这个子区间内的值构成了连续的一段.例如对于排列\(\{1,3,2 \}\),\([1, 1], [2, 2], [3, 3], [2, 3], [1, 3]\)是好的区间. 共\(q\)次询问,每次询问\(L,R\), 求有多少\(L \leq l \leq r \leq R\),满足\([l, r]\)是好的区间.\(1 \leq n, q \leq 1.2 \times 10 ^ 5\

BZOJ 1012 线段树||单调队列

非常裸的线段树  || 单调队列: 假设一个节点在队列中既没有时间优势(早点入队)也没有值优势(值更大),那么显然不管在如何的情况下都不会被选为最大值. 既然它仅仅在末尾选.那么自然能够满足以上的条件. 线段树 #include "stdio.h" #include "string.h" struct node { int l,r,Max; }data[800010]; int Max(int a,int b) { if (a<b) return b; els

【BZOJ-2892&amp;1171】强袭作战&amp;大sz的游戏 权值线段树+单调队列+标记永久化+DP

2892: 强袭作战 Time Limit: 50 Sec  Memory Limit: 512 MBSubmit: 45  Solved: 30[Submit][Status][Discuss] Description 在一个没有冬马的世界里,经历了学园祭后的春希着急着想要见到心爱的雪菜.然而在排队想见雪菜的fans太多了,春希一时半会凑不到雪菜面前. 作为高帅富,这样的问题怎么能难倒春希?春希从武也手中拿到了取自金闪闪宝库里的多啦A梦的传话筒,并且给每一个排队的fans都发了一个传话筒. 于

bzoj 1171 大sz的游戏&amp; 2892 强袭作战 (线段树+单调队列+永久性flag)

大sz的游戏 Time Limit: 50 Sec  Memory Limit: 357 MBSubmit: 536  Solved: 143[Submit][Status][Discuss] Description 大sz最近在玩一个由星球大战改编的游戏.话说绝地武士当前共控制了N个星球.但是,西斯正在暗处悄悄地准备他们的复仇计划.绝地评议会也感觉到了这件事.于是,准备加派绝地武士到各星球防止西斯的突袭.一个星球受到攻击以后,会尽快通知到总基地.需要的时间越长的星球就需要越多绝地武士来防御.为

Bzoj4826 [Hnoi2017]影魔

Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 425  Solved: 244 Description 影魔,奈文摩尔,据说有着一个诗人的灵魂.事实上,他吞噬的诗人灵魂早已成千上万.千百年来,他收集了各式各样 的灵魂,包括诗人.牧师.帝王.乞丐.奴隶.罪人,当然,还有英雄.每一个灵魂,都有着自己的战斗力,而影魔,靠 这些战斗力提升自己的攻击.奈文摩尔有 n 个灵魂,他们在影魔宽广的体内可以排成一排,从左至右标号 1 到 n. 第 i个灵魂的战斗力

【bzoj3956】Count 单调栈+可持久化线段树

题目描述 输入 输出 样例输入 3 2 0 2 1 2 1 1 1 3 样例输出 0 3 题解 单调栈+可持久化线段树 本题是 bzoj4826 的弱化版(我为什么做题总喜欢先挑难的做QAQ) $k$对点对$(i,j)$有贡献,当且仅当$a_k=max(a_{i+1},a_{i+2},...,a_{r-1})$,且$a_k<a_i\&\&a_k<a_j$. 那么我们可以使用单调栈求出i左面第一个比它大的位置$lp[i]$,和右面第一个比它大的位置$rp[i]$,那么点对$(lp

【CF671E】Organizing a Race 单调栈+线段树

[CF671E]Organizing a Race 题意:n个城市排成一排,每个城市内都有一个加油站,赛车每次经过第i个城市时都会获得$g_i$升油.相邻两个城市之间由道路连接,第i个城市和第i+1个城市之间的道路长度为$w_i$,走一单位的路要花1升油.你想在某两个城市之间举办一场锦标赛.如果你选择的两个城市分别是a和b(a<b),则具体过程如下: 1. 赛车从a开始往右走一直走到b,走过城市时会在加油站加油,走过道路时会消耗油,且一开始时就已经在a处加完油了.你需要满足赛车能有足够的油能从a

[JXOI2017]颜色 线段树扫描线 + 单调栈

---题面--- 题解: 首先题目要求删除一些颜色,换个说法就是要求保留一些颜色,那么观察到,如果我们设ll[i]和rr[i]分别表示颜色i出现的最左边的那个点和最右边的那个点,那么题目就是在要求我们选出的区间要满足区间[l, r]内所有颜色的max(rr[i]) <= r,并且min(ll[i]) >= l. 因为是区间相关的问题,又涉及到左右端点,因此我们考虑扫描线,那么考虑如何维护它. 因为每个颜色的ll[i]和rr[i]可以看做构成了一个区间,那么现在已经进入线段树的节点就分2种情况.