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\) 数组的限制:
由于 \(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;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/17237520.html
浙公网安备 33010602011771号