BZOJ 4826 HNOI2017 影魔

82 篇文章 0 订阅
13 篇文章 0 订阅

Problem

BZOJ

题外话

这道题有点类似于HNOI2016序列。好像并没有莫队做法,但是挺想打莫队的。。

Cai表示莫队被卡了:
no_more

Solution

先用单调栈处理出每个点的pre和nxt,即左边和右边第一个比更大的位置。
考虑对于点i,在区间[pre[i],nxt[i]]之间,p1只对于点对(pre[i],nxt[i])和相邻点对有贡献。当然了,pre[i]和i的贡献已经被区间内的点算过了(i和nxt[i]亦然)。
考虑p2的贡献,我们选取pre[i]和区间(i,nxt[i])之间可以做p2的贡献,等效的还有选取nxt[i]和区间(pre[i],i)之间的。
我们考虑按照坐标来进行扫描,因为如pre[i]和区间(i,nxt[i])做的p2贡献扫了l之后,还需要选取后面的数字才会生效,那么我们就设定一个生效坐标pos,作为排序依据。然后上支持区间修改的树状数组。

Code

#include <algorithm>
#include <cstdio>
#define lowbit(x) ((x)&(-(x)))
using namespace std;
typedef long long ll;
const int maxn=200010;
int n,m,p1,p2,cnt,pre[maxn],nxt[maxn],a[maxn];
ll ans[maxn],t1[maxn],t2[maxn];
struct data{
    int l,r,pos,id,val;
    bool operator < (const data &x)const{return pos<x.pos;}
}q1[maxn<<1],q2[maxn*3];
struct STACK{
    int tp,a[maxn];
    void clear(){tp=0;}
    int top(){return a[tp];}
    void push(int val){a[++tp]=val;}
    void pop(){tp--;}
}s;
template <typename Tp> inline void read(Tp &x)
{
    x=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
void input()
{
    read(n);read(m);read(p1);read(p2);
    s.clear();
    for(int i=1;i<=n;i++)
    {
        read(a[i]);
        while(s.tp&&a[s.top()]<a[i]) nxt[s.top()]=i,s.pop();
        pre[i]=s.top();s.push(i);
    }
    while(s.tp) nxt[s.top()]=n+1,s.pop();
    for(int i=1,l,r;i<=m;i++)
    {
        read(l);read(r);ans[i]+=(r-l)*p1;
        q1[i]=(data){l,r,l-1,i,-1};q1[i+m]=(data){l,r,r,i,1};
    }
    sort(q1+1,q1+m+m+1);
}
void add(int pos,int val)
{
    int ps=pos;
    while(pos<=n)
    {
        t1[pos]+=val;t2[pos]+=(ll)ps*val;
        pos+=lowbit(pos);
    }
}
ll query(int pos)
{
    ll res=0,ps=pos;
    while(pos)
    {
        res+=(ps+1)*t1[pos]-t2[pos];
        pos-=lowbit(pos);
    }
    return res;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif
    input();
    for(int i=1;i<=n;i++)
    {
        if(pre[i]&&nxt[i]<=n) q2[++cnt]=(data){pre[i],pre[i],nxt[i],0,p1};
        if(pre[i]&&nxt[i]>i+1) q2[++cnt]=(data){i+1,nxt[i]-1,pre[i],0,p2};
        if(nxt[i]<=n&&i>pre[i]+1) q2[++cnt]=(data){pre[i]+1,i-1,nxt[i],0,p2};
    }
    sort(q2+1,q2+cnt+1);
    p1=p2=1;
    while(!q1[p1].pos) p1++;
    for(int i=1;p1<=m+m&&i<=n;i++)
    {
        while(p2<=cnt&&q2[p2].pos==i)
        {
            add(q2[p2].r+1,-q2[p2].val);
            add(q2[p2].l,q2[p2].val);p2++;
        }
        while(p1<=m+m&&q1[p1].pos==i)
        {
            ans[q1[p1].id]+=q1[p1].val*(query(q1[p1].r)-query(q1[p1].l-1));
            p1++;
        }
    }
    for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值