BZOJ4826: [Hnoi2017]影魔(扫描线+树状数组)

传送门

题意:
有n个点,横纵坐标都是 1n 的排列,每次询问 [l,r] ,求在 [l,r] 中分别满足一下条件的点对 (i,j)
(定义 max(l,r) 为横坐标 l r中纵坐标的最大值)
1. y[i]>max(l,r)y[j]>max(l,r) .
2. y[i]>max(l,r)>y[j]y[j]>max(l,r)>y[i]

题解:
离线扫描线+树状数组。

先单调栈找出左右第一个纵坐标大于 y[i] L[i],R[i]

首先,对于第一种情况,设有 y[i]<y[j]
那么 j 只能是L[i]或者 R[i]
证明:若不是,则两点间有一点纵坐标大于 y[i]

对于第二种情况,容易发现所有情况都包含在了对于每个 i (L[i],j)(i<j<R[i])或者 (j,R[i])(L[i]<j<i) (画一画图就知道了)。

然后把所有合法的点对看做点或者是线段。扫描线一遍即可。

(出题人丧心病狂的卡了线段树,所以学了树状数组的区间加减查询:http://www.cnblogs.com/lcf-2000/p/5866170.html#3773822)

#include<bits/stdc++.h>
using namespace std;
struct IO
{
    streambuf *ib,*ob;
    int buf[50];
    inline void init()
    {
        ios::sync_with_stdio(false);
        cin.tie(NULL);cout.tie(NULL);
        ib=cin.rdbuf();ob=cout.rdbuf();
    }
    inline int read()
    {
        char ch=ib->sbumpc();int i=0,f=1;
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=ib->sbumpc();}
        while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=ib->sbumpc();}
        return i*f;
    }
    inline void W(long long x)
    {
        if(!x){ob->sputc('0');return;}
        if(x<0){ob->sputc('-');x=-x;}
        while(x){buf[++buf[0]]=x%10,x/=10;}
        while(buf[0]){ob->sputc(buf[buf[0]--]+'0');}
    }
}io;
const int Maxn=2e5+50;
int n,m,p1,p2,L[Maxn],R[Maxn],K[Maxn];
long long ans[Maxn];
struct Segment
{
    int l,r;
    Segment(int l,int r):l(l),r(r){}
};
inline int lowbit(int pos){return pos&(-pos);}
struct BIT
{
    long long d1[Maxn],d2[Maxn];
    inline long long asksum(int x)
    {
        long long res1=0,res2=0;
        for(int pos=x;pos;pos-=lowbit(pos))res1+=d1[pos],res2+=d2[pos];
        return res1*(x+1)-res2;
    }
    inline long long query(int l,int r){return asksum(r)-asksum(l-1);}
    inline void modify(int l,int r)
    {
        for(int pos=l;pos<=n;pos+=lowbit(pos))d1[pos]++,d2[pos]+=l;
        for(int pos=r+1;pos<=n;pos+=lowbit(pos))d1[pos]--,d2[pos]-=(r+1);
    }
}t1,t2;
struct Q
{
    int l,r,id,bz;
    Q(int l=0,int r=0,int id=0,int bz=0):l(l),r(r),id(id),bz(bz){}
};
vector<Segment>data2[Maxn];
vector<int>data[Maxn];
vector<Q>query[Maxn];
inline void GetL(int *a)
{
    static int st[Maxn],pos[Maxn],tail;
    pos[1]=1,st[1]=K[1];tail=1;
    for(int i=2;i<=n;i++)
    {
        while(st[tail]<K[i]&&tail)a[pos[tail--]]=i;
        st[++tail]=K[i],pos[tail]=i;
    }
    while(tail)a[pos[tail--]]=n+1;
}
int main()
{
    io.init();n=io.read(),m=io.read(),p1=io.read(),p2=io.read();
    for(int i=1;i<=n;i++)K[i]=io.read();
    GetL(R);reverse(K+1,K+n+1);
    GetL(L);reverse(L+1,L+n+1);
    for(int i=1;i<=n;i++)L[i]=n-L[i]+1;
    for(int i=1;i<=n;i++)
    {
        if(L[i])data[i].push_back(L[i]);
        if(R[i]!=n+1)data[i].push_back(R[i]);
        if(L[i]&&R[i]>i+1)data2[L[i]].push_back(Segment(i+1,R[i]-1));
        if(R[i]!=n+1&&L[i]<i-1)data2[R[i]].push_back(Segment(L[i]+1,i-1));
    }
    for(int i=1;i<=m;i++)
    {
        int l=io.read(),r=io.read();l--;
        if(l)query[l].push_back(Q(l+1,r,i,-1));
        query[r].push_back(Q(l+1,r,i,1));
    }
    for(int i=1;i<=n;i++)
    {
        for(int e=data[i].size()-1;e>=0;e--)t1.modify(data[i][e],data[i][e]);
        for(int e=data2[i].size()-1;e>=0;e--)t2.modify(data2[i][e].l,data2[i][e].r);
        for(int e=query[i].size()-1;e>=0;e--)
        {
            ans[query[i][e].id]+=1ll*p1*query[i][e].bz*t1.query(query[i][e].l,query[i][e].r);
            ans[query[i][e].id]+=1ll*p2*query[i][e].bz*t2.query(query[i][e].l,query[i][e].r);
        }
    }
    for(int i=1;i<=m;i++)io.W(ans[i]),io.ob->sputc('\n');
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值