peiwenjun's blog 没有知识的荒原

LOJ2773 「ROI 2017 Day 2」学习轨迹 题解

题目描述

给定一个长为 \(n\) 的数组 \(a\) ,保证 \(a_i\) 互不相同,第 \(i\) 个元素权值为 \(x_i\)

给定一个长为 \(m\) 的数组 \(b\) ,保证 \(b_i\) 互不相同,第 \(i\) 个元素权值为 \(y_i\)

你可以在 \(a,b\) 中分别选一个区间(可以不选),使得选择的位置中不包含重复数字,要求权值和最大。

输出最大权值并给出构造方案,如果某个数组不选则输出 0 0

数据范围

  • \(1\le n,m\le 5\cdot 10^5\)
  • \(1\le a_i,b_i\le n+m,1\le x_i,y_i\le 10^9\),保证 \(a_i,b_i\) 内部互不相同。

时间限制 \(\texttt{2s}\) ,空间限制 \(\texttt{512MB}\)

分析

由于我们可以选满任意一个区间,另一个区间不选,因此答案 \(\ge\) 总和一半。

因此最优方案中一定存在一个数组选择区间的权值和 \(\ge\) 这个区间总和一半,不妨设为 \(a\)

\(a\) 中前缀和第一次 \(\ge\) 一半的位置为 \(p\) ,那么 \(p\) 一定被选。

\(pos_x\)\(b_x\) 在数组 \(a\) 中出现的位置。

如果钦定 \(b\) 中选择的区间为 \([l_2,r_2]\) ,通过 \(pos\) 映射到 \(a\) 数组中并打标记,那么 \([l_1,r_1]\) 一定为从 \(p\) 往左往右扩展到的极长区间。

直接枚举 \([l_2,r_2]\) 即可做到 \(\mathcal O(n^2)\)


接下来是枚举区间的套路,对 \(r_2\) 扫描线,维护所有 \(l_2\) 的答案。

考虑单点 \(b_x\) 被选带给 \(a\) 数组的限制:

\[\begin{cases} l_1>pos_x&if(pos_x\le p)\\ r_1<pos_x&if(pos_x\ge p)\\ \end{cases} \]

由于 \(l_2\) 增大时对 \(l_1,r_1\) 的限制越,用一个单调递减的单调栈维护 \(\forall 1\le i\le r_2\)\(l_2=i\)\(l_1\) 的值及其贡献, \(r_1\) 同理只不过单调栈中的元素单调递增

在弹栈的时候更新 \(l_2\) 的答案,用线段树支持区间加即可。

输出答案可以先记录最优的 \([l_2,r_2]\) ,最后暴力扩展求出 \(l_1\)\(r_1\)

时间复杂度 \(\mathcal O((n+m)\log(n+m))\)

#include<bits/stdc++.h>
#define ll long long
#define ls p<<1
#define rs p<<1|1
#define fi first
#define se second
#define mp make_pair
#define pii pair<ll,int>
using namespace std;
const int maxn=1e6+5;
const ll inf=1e18;
int m,n,l1,r1,l2,r2;
int a[maxn],b[maxn],x[maxn],y[maxn];
int pos[maxn],vis[maxn];
int st1[maxn],st2[maxn];
ll res,s1[maxn],s2[maxn];
struct node
{
    int l,r;
    pii mx;
    ll add;
}f[maxn<<2];
void pushup(int p)
{
    f[p].mx=max(f[ls].mx,f[rs].mx);
}
void pushadd(int p,ll v)
{
    f[p].add+=v,f[p].mx.fi+=v;
}
void pushdown(int p)
{
    if(!f[p].add) return ;
    pushadd(ls,f[p].add),pushadd(rs,f[p].add),f[p].add=0;
}
void build(int p,int l,int r)
{
    f[p].l=l,f[p].r=r,f[p].add=0;
    if(l==r) return f[p].mx=mp(s1[n]-s2[l-1],l),void();
    int mid=(l+r)/2;
    build(ls,l,mid);
    build(rs,mid+1,r);
    pushup(p);
}
void modify(int p,int l,int r,ll v)
{
    if(l<=f[p].l&&f[p].r<=r) return pushadd(p,v);
    if(l>f[p].r||r<f[p].l) return ;
    pushdown(p);
    modify(ls,l,r,v);
    modify(rs,l,r,v);
    pushup(p);
}
pii query(int p,int l,int r)
{
    if(l<=f[p].l&&f[p].r<=r) return f[p].mx;
    if(l>f[p].r||r<f[p].l) return mp(-inf,0);
    pushdown(p);
    return max(query(ls,l,r),query(rs,l,r));
}
void work(int op)
{
    for(int i=1;i<=n+m;i++) pos[i]=vis[i]=0;
    for(int i=1;i<=n;i++) s1[i]=s1[i-1]+x[i],vis[a[i]]=i;
    for(int i=1;i<=m;i++) s2[i]=s2[i-1]+y[i],pos[i]=vis[b[i]];
    build(1,1,m);
    int p=1,x=0,y=0;
    while(2*s1[p]<=s1[n]) p++;
    ll cur=s1[n];
    for(int i=1,lst=0,top1=0,top2=0;i<=m;i++)
    {
        if(pos[i]==p) modify(1,1,i,-inf);
        if(pos[i]&&pos[i]<p)
        {
            while(top1&&pos[st1[top1]]<pos[i])
            {
                int l=st1[top1-1]+1,r=st1[top1];
                modify(1,l,r,s1[pos[r]]),top1--;
            }
            modify(1,st1[top1]+1,i,-s1[pos[i]]),st1[++top1]=i;
        }
        if(pos[i]&&pos[i]>p)
        {
            modify(1,lst+1,i,-s1[n]),lst=i;
            while(top2&&pos[st2[top2]]>pos[i])
            {
                int l=st2[top2-1]+1,r=st2[top2];
                modify(1,l,r,-s1[pos[r]-1]),top2--;
            }
            modify(1,st2[top2]+1,i,s1[pos[i]-1]),st2[++top2]=i;
        }
        pii now=query(1,1,i);
        if(now.fi+s2[i]>=cur) x=now.se,y=i,cur=now.fi+s2[i];
    }
    if(cur>=res)
    {
        res=cur,l2=x,r2=y;
        for(int i=1;i<=n;i++) vis[i]=0;
        for(int i=l2;i<=r2;i++) vis[pos[i]]=1;
        for(l1=p;l1>=1&&!vis[l1];l1--) ;
        for(r1=p;r1<=n&&!vis[r1];r1++) ;
        l1++,r1--;
        if(op) swap(l1,l2),swap(r1,r2);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++) scanf("%d",&x[i]);
    for(int i=1;i<=m;i++) scanf("%d",&b[i]);
    for(int i=1;i<=m;i++) scanf("%d",&y[i]);
    work(0),swap(n,m);
    for(int i=1;i<=max(n,m);i++) swap(a[i],b[i]),swap(x[i],y[i]);
    work(1);
    printf("%lld\n%d %d\n%d %d\n",res,l1,r1,l2,r2);
    return 0;
}

posted on 2023-03-20 19:53  peiwenjun  阅读(17)  评论(0)    收藏  举报

导航