题解 CF392D 【Three Arrays】

题解

首先肯定是要枚举一个的,我枚举的是中的位置)。

但我们不能从小到大枚举,要从大到小枚举。

为什么呢?从大到小枚举的话,相当于是每次会多一些限制,这样比较好维护(如果是撤销限制,那不是很麻烦吗)。

考虑会多什么限制呢?

就是那个数原来是在中被消灭的,现在不能在中被消灭了,那么就得在中被消灭。

那到底是在中还是中呢?

这就到了本题的关键地方了,我们把在中消灭的位置看成横坐标,在中消灭的位置看成纵坐标。这样的话,在二维平面中就有了一些点,我们要选一个横坐标和一个纵坐标,使得所有点要么横坐标,要么纵坐标。我们要求最小的

怎么维护二维平面上的点呢,我们用线段树下标代替横坐标,用线段树维护纵坐标。

具体怎么维护,比如一个点,那么相当于横坐标的纵坐标都必须,相当于的位置上区间覆盖最大值

由于是,所以我们相当于在线段树下标为的地方都应该加上

这就让我们不能用普通办法区间覆盖了。因为有些位置上最大值是大于要覆盖的数的,但你不知道,你会以为那个位置小,就用那个位置+覆盖的数来修改答案。

这时候,你发现随着横坐标的增大,纵坐标是单调不增的。那么我们找到第一个覆盖的值的位置(是当前要覆盖的值),然后就可以区间覆盖了,因为这时横坐标上覆盖的值都了,那么你用最小的位置+覆盖的数来修改答案就是对的。

如何找到第一个覆盖的值的位置呢?

线段树上二分即可。

细节:

可能有些是只在中出现,那么相当于都有一个下界,没关系(线段树区间查询),的话我们就区间覆盖啊,做法同上。

代码:

#include<bits/stdc++.h>
using namespace std;
#define next Next
#define last Last
const int N=1e6+5;
int n,RR,gs,ans=1e9,Xma,Yma,ma[N],a[N],b[N],c[N],d[N],La[N],Lb[N],Lc[N],lst[N],tree[N*4],mi[N*4],lazy[N*4];
char buf[1<<21],*p1=buf,*p2=buf;
inline int gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
inline int read()
{
    int ret=0,f=0;char c=gc();
    while(!isdigit(c)){if(c=='-')f=1;c=gc();}
    while(isdigit(c)){ret=ret*10+c-48;c=gc();}
    if(f)return -ret;return ret;
}
void build(int nod,int l,int r)
{
    if(l==r)
    {
        tree[nod]=mi[nod]=l;
        return;
    }
    int mid=(l+r)/2;
    build(nod*2,l,mid);
    build(nod*2+1,mid+1,r);
    tree[nod]=min(tree[nod*2],tree[nod*2+1]);
    mi[nod]=min(mi[nod*2],mi[nod*2+1]);
}
void pushdown(int nod)
{
    if(!lazy[nod])return;
    mi[nod*2]=max(mi[nod*2],tree[nod*2]+lazy[nod]);
    mi[nod*2+1]=max(mi[nod*2+1],tree[nod*2+1]+lazy[nod]);
    lazy[nod*2]=max(lazy[nod*2],lazy[nod]);
    lazy[nod*2+1]=max(lazy[nod*2+1],lazy[nod]);
    ma[nod*2]=max(ma[nod*2],lazy[nod]);
    ma[nod*2+1]=max(ma[nod*2+1],lazy[nod]);
    lazy[nod]=0;
}
void change(int nod,int l,int r,int L,int R,int val)
{
    if(l==L&&r==R)
    {
        ma[nod]=max(ma[nod],val);
        mi[nod]=max(mi[nod],tree[nod]+val);
        lazy[nod]=max(lazy[nod],val);
        return;
    }
    pushdown(nod);
    int mid=(l+r)/2;
    if(R<=mid)change(nod*2,l,mid,L,R,val);
    else if(L>mid)change(nod*2+1,mid+1,r,L,R,val);
    else{
        change(nod*2,l,mid,L,mid,val);
        change(nod*2+1,mid+1,r,mid+1,R,val);
    }
    mi[nod]=min(mi[nod*2],mi[nod*2+1]);
    ma[nod]=min(ma[nod*2],ma[nod*2+1]);
}
int find(int nod,int l,int r,int L,int R)
{
    if(l==L&&r==R)return mi[nod];
    pushdown(nod);
    int mid=(l+r)/2;
    if(R<=mid)return find(nod*2,l,mid,L,R);
    else if(L>mid)return find(nod*2+1,mid+1,r,L,R);
    else return min(find(nod*2,l,mid,L,mid),find(nod*2+1,mid+1,r,mid+1,R));
}
int query(int nod,int l,int r,int val)
{
    if(l==r)
    {
        if(ma[nod]>=val)return l+1;
        else return l;
    }
    pushdown(nod);
    int mid=(l+r)/2;
    if(ma[nod*2]>=val)return query(nod*2+1,mid+1,r,val);
    else return query(nod*2,l,mid,val);
}
signed main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
        d[++gs]=a[i];
    }
    for(int i=1;i<=n;i++)
    {
        b[i]=read();
        d[++gs]=b[i];
    }
    for(int i=1;i<=n;i++)
    {
        c[i]=read();
        d[++gs]=c[i];
    }
    sort(d+1,d+gs+1);
    gs=unique(d+1,d+gs+1)-d-1;
    for(int i=1;i<=n;i++)a[i]=lower_bound(d+1,d+gs+1,a[i])-d;
    for(int i=1;i<=n;i++)b[i]=lower_bound(d+1,d+gs+1,b[i])-d;
    for(int i=1;i<=n;i++)c[i]=lower_bound(d+1,d+gs+1,c[i])-d;
    for(int i=1;i<=gs;i++)lst[i]=0;
    for(int i=1;i<=n;i++)
    {
        if(lst[a[i]])continue;
        lst[a[i]]=1;
        La[a[i]]=i;
    }
    for(int i=1;i<=gs;i++)lst[i]=0;
    for(int i=1;i<=n;i++)
    {
        if(lst[b[i]])continue;
        lst[b[i]]=1;
        Lb[b[i]]=i;
    }
    for(int i=1;i<=gs;i++)lst[i]=0;
    for(int i=1;i<=n;i++)
    {
        if(lst[c[i]])continue;
        lst[c[i]]=1;
        Lc[c[i]]=i;
    }
    build(1,0,n);
    for(int i=1;i<=n;i++)
        if((!La[b[i]])&&(Lb[b[i]]==i))
        {
            if(!Lc[b[i]])Xma=max(Xma,i);
        }
    for(int i=1;i<=n;i++)
        if((!La[c[i]])&&(Lc[c[i]]==i))
        {
            if(!Lb[c[i]])Yma=max(Yma,i);
        }
    for(int i=1;i<=n;i++)
        if((!La[b[i]])&&(Lb[b[i]]==i))
        {
            if(Lc[b[i]])
            {
                int x=query(1,0,n,Lc[b[i]]);
                if(x<=i-1)change(1,0,n,x,i-1,Lc[b[i]]);
            }
        }
    int x=query(1,0,n,Yma);
    if(x<=n)change(1,0,n,x,n,Yma);
    ans=min(ans,n+max(Xma+Yma,find(1,0,n,Xma,n)));
    for(int i=n;i;i--)
    {
        if(La[a[i]]==i)
        {

            if(!Lb[a[i]])
            {
                if(!Lc[a[i]])break;
                Yma=max(Yma,Lc[a[i]]);
            }
            else if(!Lc[a[i]])Xma=max(Xma,Lb[a[i]]);
            else{
                int x=query(1,0,n,Lc[a[i]]);
                if(x<=Lb[a[i]]-1)change(1,0,n,x,Lb[a[i]]-1,Lc[a[i]]);
            }
        }
        int x=query(1,0,n,Yma);
        if(x<=n)change(1,0,n,x,n,Yma);
        ans=min(ans,i-1+max(Xma+Yma,find(1,0,n,Xma,n)));
    }
    cout<<ans;
}
/*
枚举x
然后就是线段树维护 
*/