zoukankan      html  css  js  c++  java
  • 【BZOJ4826】影魔(AHOI&HNOI2017)-线段树+离线

    测试地址:影魔
    做法:本题需要用到线段树+离线。
    首先你需要注意到,题目中所给的序列是一个全排列(没看到这个条件的我直接跪了)。然后我们转化题目中的限制条件,我们发现这个条件等价于:当ki,kj分别为[i,j]的最大和次大值时,有p1的贡献;当ki,kj中有一个是[i,j]的最大值,而另一个不是次大值时,有p2的贡献。
    对于点对的贡献,我们考虑两种情况:
    一:贡献为p1的情况。注意到此时,左端点和右端点之间存在一个最大的ks(l<s<r),那么kl,kr应该分别是ks左右距离它最近的比它大的元素。
    二:贡献为p2的情况。注意到此时,左端点和右端点之间存在一个最大的ks(l<s<r),分类讨论,如果kl>kr,那么kl应是ks向左距离它最近的比它大的元素,又因为kskr之间没有比ks大的数,也就是说krks向右距离它最近的比它大的元素左边,而kl<kr可以同理分析。
    于是我们发现了一种新的统计贡献的方式:枚举ks,找到它向左和向右距离它最近的比它大的元素(用单调栈或者O(nlogn)的数据结构都可以),按照上面的分析统计贡献。注意到上面的贡献都是一个端点固定,另一个端点在一个连续区间中移动的情况,所以我们可以把这些贡献看成固定点对移动点的贡献。又想到一个区间[l,r]中所有点对的总贡献,等于区间[1,r]中的点对该区间的总贡献,减去区间[1,l1]中的点对该区间的总贡献,所以我们离线,再配合线段树维护即可。于是我们就解决了这一题,时间复杂度为O(nlogn)
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n,m,a[200010],l[200010],r[200010],st[200010],top,tot;
    int ql[200010],qr[200010];
    ll p1,p2,seg[800010]={0},tag[800010]={0},ans[200010];
    bool vis[200010]={0};
    struct oper
    {
        int pos,l,r;
        ll c;
    }p[600010];
    struct event
    {
        int id,pos;
    }q[400010];
    bool cmp(oper a,oper b) {return a.pos<b.pos;}
    bool cmpe(event a,event b) {return a.pos<b.pos;}
    
    void insert(int pos,int l,int r,ll c)
    {
        p[++tot].pos=pos;
        p[tot].l=l,p[tot].r=r;
        p[tot].c=c;
    }
    
    void init()
    {
        scanf("%d%d%lld%lld",&n,&m,&p1,&p2);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
    
        top=0;
        for(int i=1;i<=n;i++)
        {
            while(top&&a[st[top]]<a[i])
            {
                r[st[top]]=i;
                top--;
            }
            if (top) l[i]=st[top];
            else l[i]=0;
            st[++top]=i;
        }
        while(top)
        {
            r[st[top]]=n+1;
            top--;
        }
    
        tot=0;
        for(int i=1;i<=n;i++)
        {
            if (l[i]!=0&&r[i]!=n+1&&l[i]!=r[i]) insert(l[i],r[i],r[i],p1);
            if (l[i]!=0&&i+1<=r[i]-1) insert(l[i],i+1,r[i]-1,p2);
            if (r[i]!=n+1&&l[i]+1<=i-1) insert(r[i],l[i]+1,i-1,p2);
        }
        sort(p+1,p+tot+1,cmp);
    }
    
    void pushdown(int no,ll l,ll r)
    {
        if (tag[no]!=0)
        {
            ll mid=(l+r)>>1;
            tag[no<<1]+=tag[no],tag[no<<1|1]+=tag[no];
            seg[no<<1]+=(mid-l+1)*tag[no];
            seg[no<<1|1]+=(r-mid)*tag[no];
            tag[no]=0;
        }
    }
    
    void pushup(int no)
    {
        seg[no]=seg[no<<1]+seg[no<<1|1];
    }
    
    void add(int no,ll l,ll r,int s,int t,ll c)
    {
        if (l>=s&&r<=t)
        {
            tag[no]+=c;
            seg[no]+=(r-l+1)*c;
            return;
        }
        ll mid=(l+r)>>1;
        pushdown(no,l,r);
        if (s<=mid) add(no<<1,l,mid,s,t,c);
        if (t>mid) add(no<<1|1,mid+1,r,s,t,c);
        pushup(no);
    }
    
    ll query(int no,ll l,ll r,int s,int t)
    {
        if (l>=s&&r<=t) return seg[no];
        ll sum=0,mid=(l+r)>>1;
        pushdown(no,l,r);
        if (s<=mid) sum+=query(no<<1,l,mid,s,t);
        if (t>mid) sum+=query(no<<1|1,mid+1,r,s,t);
        return sum;
    }
    
    void work()
    {
        for(int i=1;i<=m;i++)
        {
            int x=(i<<1)-1,y=(i<<1);
            scanf("%d%d",&ql[i],&qr[i]);
            q[x].pos=ql[i]-1,q[y].pos=qr[i];
            q[x].id=q[y].id=i;
        }
        sort(q+1,q+(m<<1)+1,cmpe);
    
        int now=1,nowa=1;
        for(int i=1;i<=(m<<1);i++)
        {
            while(nowa<=q[i].pos) add(1,1,n,nowa+1,nowa+1,p1),nowa++;
            while(now<=tot&&p[now].pos<=q[i].pos)
            {
                add(1,1,n,p[now].l,p[now].r,p[now].c);
                now++;
            }
            if (!vis[q[i].id])
            {
                ans[q[i].id]=query(1,1,n,ql[q[i].id],qr[q[i].id]);
                vis[q[i].id]=1;
            }
            else ans[q[i].id]=query(1,1,n,ql[q[i].id],qr[q[i].id])-ans[q[i].id];
        }
    
        for(int i=1;i<=m;i++)
            printf("%lld
    ",ans[i]);
    }
    
    int main()
    {
        init();
        work();
    
        return 0;
    }
  • 相关阅读:
    ubuntu系统上常用的开发工具
    wamp环境下安装pear
    PHP中preg_match_all函数用法使用详解
    晚睡对策
    iphone相关
    090213 阴
    月曜日の予定(10:30までREVIEW  10:00まで完成予定)
    一个通知
    我还没走
    星期5
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793379.html
Copyright © 2011-2022 走看看