【BZOJ4826】【HNOI2017】影魔

题目描述

给定一个长度 n2×105 的序列a,对于点对 (i,j) 若不存在 i<k<j 使得 a[i]<a[k]a[j]<a[k] ,则点对向答案贡献 p1 ,若存在 i<k<j 使得 a[i]<a[k]a[k]<a[j] ,或 a[i]>a[k]a[k]>a[j] 时,点对向答案贡献 p2
现在有 m2×105 个询问,每次查询区间 [a,b] 中的点对向答案的贡献之和。

题目解析

首先,我们的第一想法就枚举每一个位置作为 k ,看左边有多少个小于/大于,右边有多少个小于/大于a[k],两边乘起来就是 p1,p2 的数量,如果我们在想一想,若设位置 i 左边比他大的第一个为li,右边第一个比他大的是 ri 。则对于贡献 p1 ,位置 i 产生的点对便是(li,ri),注意只产生一个,至于为什么,我们可以这样理解:若 li 左端还有一个 l ,使得 (l,ri) 组成一个贡献点对,那么此时 (l,ri) 间的最大值是 li ,即我们一定在 li 处讨论过了;对于贡献 p2 ,我们也只考虑 (li,i+1ri1) (li+1i1,ri) 的点对。

又因为我们对于每一个询问都需要回答,于是我们便可以将贡献点对映射到 x,y 轴上,用主席树维护区间增加,区间查询,至于在 x 轴上区间加可以转换为在另一棵树上y轴上区间加。

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
using namespace std;

#define MAXN 200000
#define MAXLOG 18
#define MAXM 200000
#define INF 0x3f3f3f3f
typedef long long int LL;

template<class T>
void Read(T &x){
    x=0;char c=getchar();bool flag=0;
    while(c<'0'||'9'<c){if(c=='-')flag=1;c=getchar();}
    while('0'<=c&&c<='9'){x=x*10+c-'0';c=getchar();}
    if(flag)x=-x;
}

struct node{
    LL val,lazy;
    int ls,rs;
}Leaf[MAXM*MAXLOG*5+100];
int New_Leaf=0;
int NewNode(){
    int x=++New_Leaf;
    Leaf[x].ls=Leaf[x].rs=Leaf[x].val=Leaf[x].lazy=0;
    return x;
}

void insert(int ll,int rr,int l,int r,int w,int &x,int last){
    x=NewNode();
    Leaf[x]=Leaf[last];
    if(ll==l&&rr==r){
        Leaf[x].lazy+=w;
        return;
    }
    else{
        Leaf[x].val+=1ll*w*(r-l+1);

        int mid=(ll+rr)>>1;
        if(r<=mid)insert(ll,mid,l,r,w,Leaf[x].ls,Leaf[last].ls);
        else if(mid<l)insert(mid+1,rr,l,r,w,Leaf[x].rs,Leaf[last].rs);
        else insert(ll,mid,l,mid,w,Leaf[x].ls,Leaf[last].ls),
             insert(mid+1,rr,mid+1,r,w,Leaf[x].rs,Leaf[last].rs);
    }
}

LL query(int ll,int rr,int l,int r,int x){
    if(!x)return 0;
    if(l<=ll&&rr<=r)
        return Leaf[x].val+Leaf[x].lazy*(rr-ll+1);
    else{
        int mid=(ll+rr)>>1;
        if(r<=mid)return Leaf[x].lazy*(r-l+1)+query(ll,mid,l,r,Leaf[x].ls);
        else if(mid<l)return Leaf[x].lazy*(r-l+1)+query(mid+1,rr,l,r,Leaf[x].rs);
        else return Leaf[x].lazy*(r-l+1)+query(ll,mid,l,mid,Leaf[x].ls)+query(mid+1,rr,mid+1,r,Leaf[x].rs);
    }
}

int root1[MAXN+10],root2[MAXN+10];
int A[MAXN+10];
int l[MAXN+10],r[MAXN+10];
int n,m,p1,p2;
stack<int>stk;

struct abcd{
    int l,r,w;
    abcd(){}
    abcd(int a,int b,int c):l(a),r(b),w(c){}
};
vector<abcd>hehe1[MAXN+10],hehe2[MAXN+10];

void init(){
    while(!stk.empty())stk.pop();
    stk.push(1),l[1]=0;
    for(int i=2;i<=n;++i){
        while(!stk.empty()&&A[stk.top()]<A[i])r[stk.top()]=i,stk.pop();
        if(stk.empty())l[i]=0;
        else l[i]=stk.top();
        stk.push(i);
    }
    while(!stk.empty())r[stk.top()]=n+1,stk.pop();

    for(int i=1;i<=n;++i)hehe1[i].clear(),hehe2[i].clear();
    for(int i=1;i<=n;++i){
        //printf("[%d] l:%d r:%d\n",i,l[i],r[i]);
        if(l[i]>0&&r[i]<=n)hehe1[l[i]].push_back(abcd(r[i],r[i],p1));
        if(l[i]>0&&i<r[i]-1)hehe1[l[i]].push_back(abcd(i+1,r[i]-1,p2));
        if(r[i]<=n&&i>l[i]+1)hehe2[r[i]].push_back(abcd(l[i]+1,i-1,p2));
    }
}

int main(){
    Read(n),Read(m),Read(p1),Read(p2);
    for(int i=1;i<=n;++i)Read(A[i]);

    init();

    memset(root1,0,sizeof(root1));
    memset(root2,0,sizeof(root2));
    for(int i=1;i<=n;++i){
        root1[i]=root1[i-1];
        for(int j=0;j<hehe1[i].size();++j){
            insert(1,n,hehe1[i][j].l,hehe1[i][j].r,hehe1[i][j].w,root1[i],root1[i]);
            //printf("Line1 : %d (%d->%d) w:%d\n",i,hehe1[i][j].l,hehe1[i][j].r,hehe1[i][j].w);
        }
        if(i<n)insert(1,n,i+1,i+1,p1,root1[i],root1[i]);
    }
    for(int i=1;i<=n;++i){
        root2[i]=root2[i-1];
        for(int j=0;j<hehe2[i].size();++j){
            insert(1,n,hehe2[i][j].l,hehe2[i][j].r,hehe2[i][j].w,root2[i],root2[i]);
            //printf("Line2 : %d (%d->%d) w:%d\n",i,hehe2[i][j].l,hehe2[i][j].r,hehe2[i][j].w);
        }
    }

    int a,b;
    LL ans;
    for(int i=1;i<=m;++i){
        Read(a),Read(b);
        ans=0;
        ans+=query(1,n,a,b,root1[b]);
        ans-=query(1,n,a,b,root1[a-1]);//printf("%I64d\n",ans);
        ans+=query(1,n,a,b,root2[b]);
        ans-=query(1,n,a,b,root2[a-1]);

        printf("%lld\n",ans);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值