bzoj4826 hnoi2017影魔

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时,若 maxk[s]|i<s<j<=mink[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

输入文件名为sf.in。
第一行n,m,p1,p2
第二行n个数:k[1],k[2],…,k[n]
接下来m行,每行两个数a,b,表示询问区间[a,b]中的灵魂对会为影魔提供多少攻击力。

Output

输出文件名为sf.out
共输出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

Data Constraint

30%:1<= n,m <= 500。
另30%: p1=2*p2。
100%:1 <= n,m <= 200000;1 <= p1,p2 <= 1000。

题解

正常一点的解法就是主席树强行维护一波咯,但是我并不会区间加入(为什么我怎么想都觉得会爆空间呢?),本来以为这题不可改,但是良心的题解提供了另外一种很优秀的解法
我们考虑用扫描线来解决这个问题,我们可以把某一个点作为左端点和右端点的贡献(而且是较高的那个端点)分开来考虑
首先用单调栈处理出每一个点的下一个和上一个比它高的位置
设为next[i]以及last[i]
对于左端点:
假设我们当前的扫描线扫描到了i,那么我们就先钦定[i+1,next[i]]这一段都获得p2的贡献,而在next[i]右边的点肯定是和i没有关系的,就算有(比如说比next[i]还要高的点)也会在计算以那个点为右端点的时候被计算到,这样就可以巧妙的去重了
然后是关键,如果有next[i],我们将它单点加p1-2*p2,为什么要这样加呢?
我们可以用一个例子来解释
设现在有两个位置i,j且这个点对的贡献为p1,由于给的序列是一个排列,所以肯定有其中一个大于另外一个
那么以i为左端点以及以j为右端点时都会给对方p2的贡献,加起来也就是2*p2,所以我们要把它约掉,然后再加上p1
最后由于我们是扫描线,所以说每次做之前要先把询问数组拍一下序,然后对于左端点,我们从右到左扫,贡献就是当前线段树的[1,q[i,2]]这些叶子节点的贡献和,类似的,对于右端点,我们的扫描线从左往右扫,贡献就是[q[i,1],n]

其实主要的思路就是先通过扫描线搞定其中的一个限制,然后把贡献放在线段树上与另外一个限制有关的位置放上相应的贡献,通过区间的操作维护答案

贴代码

var
    tree,lazy:array[0..700005]of int64;
    next,last,gt,st:array[0..200005]of longint;
    ans:array[0..200005]of int64;
    q:array[0..200005,1..3]of longint;
    i,j,k,l,m,n,p1,p2,x,y,z,p:longint;
    tot:int64;
procedure qsort(l,r:longint);
var
    i,j,mid:longint;
begin
    i:=l;
    j:=r;
    mid:=q[(i+j) div 2,1];
    repeat
        while q[i,1]<mid do inc(i);
        while q[j,1]>mid do dec(j);
        if i<=j then
        begin
            q[0]:=q[i];
            q[i]:=q[j];
            q[j]:=q[0];
            inc(i);
            dec(j);
        end;
    until i>j;
    if i<r then qsort(i,r);
    if l<j then qsort(l,j);
end;
procedure qsort1(l,r:longint);
var
    i,j,mid:longint;
begin
    i:=l;
    j:=r;
    mid:=q[(i+j) div 2,2];
    repeat
        while q[i,2]<mid do inc(i);
        while q[j,2]>mid do dec(j);
        if i<=j then
        begin
            q[0]:=q[i];
            q[i]:=q[j];
            q[j]:=q[0];
            inc(i);
            dec(j);
        end;
    until i>j;
    if i<r then qsort1(i,r);
    if l<j then qsort1(l,j);
end;
procedure fi(l,r,v:longint);
begin
    tree[v]:=tree[v]+(r-l+1)*lazy[v];
    if l<>r then
    begin
        inc(lazy[v*2],lazy[v]);
        inc(lazy[v*2+1],lazy[v]);
    end;
    lazy[v]:=0;
end;
procedure change(v,l,r,x,y,z:longint);
var
    mid:longint;
begin
    tree[v]:=tree[v]+(r-l+1)*lazy[v];
    if l<>r then
    begin
        inc(lazy[v*2],lazy[v]);
        inc(lazy[v*2+1],lazy[v]);
    end;
    lazy[v]:=0;
    if (l=x) and (r=y) then
    begin
        tree[v]:=tree[v]+(r-l+1)*z;
        if l<>r then
        begin
            lazy[v*2]:=lazy[v*2]+z;
            lazy[v*2+1]:=lazy[v*2+1]+z;
        end;
    end
    else
    begin
        mid:=(l+r) div 2;
        if y<=mid then change(v*2,l,mid,x,y,z) else
        if x>mid then change(v*2+1,mid+1,r,x,y,z) else
        begin
            change(v*2,l,mid,x,mid,z);
            change(v*2+1,mid+1,r,mid+1,y,z);
        end;
        if lazy[v*2+1]<>0 then
        begin
        fi(mid+1,r,v*2+1);
        end;
        if lazy[v*2]<>0 then fi(l,mid,v*2);
        tree[v]:=tree[v*2]+tree[v*2+1];
    end;
end;
procedure find(v,l,r,x,y:longint);
var
    mid:longint;
begin
    tree[v]:=tree[v]+(r-l+1)*lazy[v];
    if l<>r then
    begin
        inc(lazy[v*2],lazy[v]);
        inc(lazy[v*2+1],lazy[v]);
    end;
    lazy[v]:=0;
    if (l=x) and (r=y) then tot:=tot+tree[v] else
    begin
        mid:=(l+r) div 2;
        if y<=mid then find(v*2,l,mid,x,y) else
        if x>mid then find(v*2+1,mid+1,r,x,y) else
        begin
            find(v*2,l,mid,x,mid);
            find(v*2+1,mid+1,r,mid+1,y);
        end;
    end;
end;
begin
    assign(input,'sf.in'); reset(input);
    assign(output,'sf.out'); rewrite(output);
    readln(n,m,p1,p2);
    for i:=1 to n do read(gt[i]);
    readln;
    x:=1;
    st[1]:=1;
    for i:=2 to n do
    begin
        while (gt[i]>gt[st[x]]) and (x>0) do
        begin
            next[st[x]]:=i;
            dec(x);
        end;
        inc(x);
        st[x]:=i;
    end;
    x:=1;
    st[1]:=n;
    for i:=n-1 downto 1 do
    begin
        while (gt[i]>gt[st[x]]) and (x>0) do
        begin
            last[st[x]]:=i;
            dec(x);
        end;
        inc(x);
        st[x]:=i;
    end;
    for i:=1 to m do readln(q[i,1],q[i,2]);
    for i:=1 to m do q[i,3]:=i;
    qsort(1,m);
    x:=m;
    for i:=n downto 1 do
    begin
        if next[i]<>0 then
        begin
            change(1,1,n,i+1,next[i],p2);
            change(1,1,n,next[i],next[i],p1-2*p2);
        end else
        if i<>n then
        change(1,1,n,i+1,n,p2);
        while i=q[x,1] do
        begin
            tot:=0;
            find(1,1,n,1,q[x,2]);
            ans[q[x,3]]:=ans[q[x,3]]+tot;
            dec(x);
            if x=0 then break;
        end;
        if x=0 then break;
    end;
    fillchar(tree,sizeof(tree),0);
    fillchar(lazy,sizeof(lazy),0);
    qsort1(1,m);
    x:=1;
    for i:=1 to n do
    begin
        if last[i]<>0 then
        begin
            change(1,1,n,last[i],i-1,p2);
            change(1,1,n,last[i],last[i],p1-2*p2);
        end else
        if i<>1 then
        change(1,1,n,1,i-1,p2);
        while i=q[x,2] do
        begin
            tot:=0;
            find(1,1,n,q[x,1],n);
            ans[q[x,3]]:=ans[q[x,3]]+tot;
            inc(x);
            if x>m then break;
        end;
        if x>m then break;
    end;
    for i:=1 to m do writeln(ans[i]);
    close(input); close(output);
end.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值