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

4826: [Hnoi2017]影魔

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 669  Solved: 384
[Submit][Status][Discuss]

Description

影魔,奈文摩尔,据说有着一个诗人的灵魂。事实上,他吞噬的诗人灵魂早已成千上万。千百年来,他收集了各式各样

的灵魂,包括诗人、牧师、帝王、乞丐、奴隶、罪人,当然,还有英雄。每一个灵魂,都有着自己的战斗力,而影魔,靠

这些战斗力提升自己的攻击。奈文摩尔有 n 个灵魂,他们在影魔宽广的体内可以排成一排,从左至右标号 1 到 n。

第 i个灵魂的战斗力为 k[i],灵魂们以点对的形式为影魔提供攻击力,对于灵魂对 i,j(i<j)来说,若不存在 k[s](i

<s<j)大于 k[i]或者 k[j],则会为影魔提供 p1 的攻击力(可理解为:当 j=i+1 时,因为不存在满足 i<s<j 的 s,从

而 k[s]不存在,这时提供 p1 的攻击力;当 j>i+1 时,若max{k[s]|i<s<j}<=min{k[i],k[j]} , 则 提 供 p1 的 攻

击 力 ); 另 一 种 情 况 , 令 c 为k[i+1],k[i+2],k[i+3]......k[j-1]的最大值,若 c 满足:k[i]<c<k[j],或

者 k[j]<c<k[i],则会为影魔提供 p2 的攻击力,当这样的 c 不存在时,自然不会提供这 p2 的攻击力;其他情况的

点对,均不会为影魔提供攻击力。影魔的挚友噬魂鬼在一天造访影魔体内时被这些灵魂吸引住了,他想知道,对于任

意一段区间[a,b],1<=a<b<=n,位于这些区间中的灵魂对会为影魔提供多少攻击力,即考虑 所有满足a<=i<j<=b 的灵

魂对 i,j 提供的攻击力之和。顺带一提,灵魂的战斗力组成一个 1 到 n 的排列:k[1],k[2],...,k[n]。

Input

第一行 n,m,p1,p2

第二行 n 个数:k[1],k[2],...,k[n]

接下来 m 行,每行两个数 a,b,表示询问区间[a,b]中的灵魂对会为影魔提供多少攻击力。

1 <= n,m <= 200000;1 <= p1,p2 <= 1000

Output

共输出 m 行,每行一个答案,依次对应 m 个询问。

Sample Input

10 5 2 3

7 9 5 1 3 10 6 8 2 4

1 7

1 9

1 3

5 9

1 5

Sample Output

30

39

4

13

16

HINT

Source

[Submit][Status][Discuss]

比较巧妙的地方是将询问映射到二维坐标系里,虽然比较套路但考场上不一定能想到。剩下的就是主席树裸题了。

代码用时:40min。

#include<cstdio>
#include<vector>
#include<algorithm>
#define rep(i,l,r) for (int i=l; i<=r; i++)
typedef long long ll;
using namespace std;

const int N=200100;
int n,m,p1,p2,l,r,cnt,top,rt[N],a[N],L[N],R[N],stk[N],v[N*80],ls[N*80],rs[N*80];
ll s[N*80];
struct P{ int x,y; ll p; };
vector<P>V[N];

void mdf(int y,int &x,int L,int R,int l,int r,int k){
    v[x=++cnt]=v[y]; s[x]=s[y]; ls[x]=ls[y]; rs[x]=rs[y];
    s[x]+=1ll*(r-l+1)*k;
    if (l==L && r==R){ v[x]+=k; return; }
    int mid=(L+R)>>1;
    if (r<=mid) mdf(ls[y],ls[x],L,mid,l,r,k);
    else if (l>mid) mdf(rs[y],rs[x],mid+1,R,l,r,k);
        else mdf(ls[y],ls[x],L,mid,l,mid,k),mdf(rs[y],rs[x],mid+1,R,mid+1,r,k);
}

ll que(int x,int L,int R,int l,int r){
    if (!x) return 0;
    if (l==L && r==R) return s[x];
    int mid=(L+R)>>1; ll sum=1ll*(r-l+1)*v[x];
    if (r<=mid) return sum+que(ls[x],L,mid,l,r);
    else if (l>mid) return sum+que(rs[x],mid+1,R,l,r);
        else return sum+que(ls[x],L,mid,l,mid)+que(rs[x],mid+1,R,mid+1,r);
}

int main(){
    scanf("%d%d%d%d",&n,&m,&p1,&p2);
    rep(i,1,n) scanf("%d",&a[i]);
    rep(i,1,n){
        while (top && a[stk[top]]<a[i]) top--;
        L[i]=stk[top]; stk[++top]=i;
    }
    rep(i,0,top) stk[i]=0; stk[top=0]=n+1;
    for (int i=n; i; i--){
        while (top && a[stk[top]]<a[i]) top--;
        R[i]=stk[top]; stk[++top]=i;
    }
    rep(i,1,n){
        if (i<n) V[i].push_back((P){i+1,i+1,p1});
        if (L[i] && R[i]<=n) V[L[i]].push_back((P){R[i],R[i],p1});
        if (L[i] && i<=R[i]-2) V[L[i]].push_back((P){i+1,R[i]-1,p2});
        if (R[i]<=n && i>=L[i]+2) V[R[i]].push_back((P){L[i]+1,i-1,p2});
    }
    rt[0]=0;
    rep(i,1,n){
        rt[i]=rt[i-1];
        for (vector<P>::iterator it=V[i].begin(); it!=V[i].end(); it++)
            mdf(rt[i],rt[i],1,n,it->x,it->y,it->p);
    }
    rep(i,1,m) scanf("%d%d",&l,&r),printf("%lld\n",que(rt[r],1,n,l,r)-que(rt[l-1],1,n,l,r));
    return 0;
}

原文地址:https://www.cnblogs.com/HocRiser/p/8323582.html

时间: 2024-10-12 10:54:50

[BZOJ4826][HNOI2017]影魔(主席树)的相关文章

Bzoj4826 [Hnoi2017]影魔

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

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

题目链接 BZOJ4826 题解 蒟蒻智力水平捉急orz 我们会发现相邻的\(i\)和\(j\)贡献一定是\(p1\),可以很快算出来[然而我一开始忘了考虑调了半天] 我们现在只考虑不相邻的 我们只需要找出所有产生贡献的\(i,j\)即可 我们发现每一个产生贡献的\(i,j\)都能对应到一个三元组\((i,k,j)\),分别对应区间的最大值,次大值,第三大值 我们枚举中间位置\(i\),找到\(i\)左边第一个比\(i\)大的位置\(L[i]\),右边第一个比\(i\)大的位置\(R[i]\)

[BZOJ 4826]影魔 区间修改主席树 标记永久化

为了这道题还特地去学了标记永久化,可能对于区间修改主席树或者树套树比较有用吧OvO 我们可以把答案分为两部分:p1造成的和p2造成的 我们枚举序列,用单调栈求出序列每一个位置i,左右边第一个比它大的L,R 开三棵主席树tree1 tree2 tree3 把L扔进tree1的R位置(单点+1),L+1~i-1扔进tree2的R位置,i+1~R-1扔进tree3的L位置(区间+1) 然后询问[l,r]的时候,求出三棵区间主席树 p1造成的贡献为区间tree1内大于等于L的个数 p2造成的贡献为区间t

[poj2104]可持久化线段树入门题(主席树)

解题关键:离线求区间第k小,主席树的经典裸题: 对主席树的理解:主席树维护的是一段序列中某个数字出现的次数,所以需要预先离散化,最好使用vector的erase和unique函数,很方便:如果求整段序列的第k小,我们会想到离散化二分和线段树的做法, 而主席树只是保存了序列的前缀和,排序之后,对序列的前缀分别做线段树,具有差分的性质,因此可以求任意区间的第k小,如果主席树维护索引,只需要求出某个数字在主席树中的位置,即为sort之后v中的索引:若要求第k大,建树时反向排序即可 1 #include

【BZOJ 3551】[ONTAK2010] Peaks加强版 Kruskal重构树+树上倍增+主席树

这题真刺激...... I.关于Kruskal重构树,我只能开门了,不过补充一下那玩意还是一棵满二叉树.(看一下内容之前请先进门坐一坐) II.原来只是用树上倍增求Lca,但其实树上倍增是一种方法,Lca只是他的一种应用,他可以搞各种树上问题,树上倍增一般都会用到f数组. |||.我们跑出来dfs序就能在他的上面进行主席树了. IV.别忘了离散. V.他可能不连通,我一开始想到了,但是我觉得出题人可能会是好(S)人(B),但是...... #include <cstdio> #include

[bzoj3932][CQOI2015]任务查询系统-题解[主席树][权值线段树]

Description 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分.超级计算机中的 任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第Ei秒后结束(第Si秒和Ei秒任务也在运行 ),其优先级为Pi.同一时间可能有多个任务同时执行,它们的优先级可能相同,也可能不同.调度系统会经常向 查询系统询问,第Xi秒正在运行的任务中,优先级最小的Ki个任务(即将任务按照优先级从小到大排序后取前Ki个 )的优先级之和是多少.特别的,如

BZOJ_3207_花神的嘲讽计划1_(Hash+主席树)

描述 http://www.lydsy.com/JudgeOnline/problem.php?id=3207 给出一个长度为\(n\)的串,以及\(m\)个长度为\(k\)的串,求每个长度为\(k\)的串在原串\([x,y]\)区间是否出现过. 分析 这道题要求对比长度为\(k\)的串,于是我们把这些串的Hash值都算出来,问题就转化成了求\([x,y]\)的区间中是否出现过某Hash值. 求区间中某一个值出现了多少次,可以用主席树. p.s. 1.学习了主席树指针的写法,比数组慢好多啊...

[主席树]ZOJ3888 Twelves Monkeys

题意:有n年,其中m年可以乘时光机回到过去,q个询问 下面m行,x,y 表示可以在y年穿越回x年, 保证y>x 下面q个询问, 每个询问有个年份k 问的是k年前面 有多少年可以通过一种以上($\ge 2$)方法穿越回去的, 其中时光机只能用一次 比如案例 9 3 3 9 1 6 1 4 1 6 7 2 如图 对于询问 6这一年:1.穿越回第1年  2.等时间过呀过呀过到第9年,再穿越回第1年 那么第1年就有两种方法可以穿越回去, 同理, 2.3.4年也有同样两种方法(回到1再等时间过呀过 过到2

POJ2104主席树模板题

完成新成就——B站上看了算法https://www.bilibili.com/video/av4619406/?from=search&seid=17909472848554781180#page=2 K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 60158   Accepted: 21054 Case Time Limit: 2000MS Description You are working