【CF392D】Three Arrays-set+multiset

测试地址:Three Arrays
题目大意:有三个长为n的数列A,B,C,要求从A中取前a个数,从B中取前b个数,从C中取前c个数,使得取出的数的并集和这三个数列的并集相等,求最小的a+b+c
做法:本题需要用到set+multiset。
这题也是清北学堂讲的,思想非常不错,写在这里跟大家分享。
考虑只有两个数列怎么做,先预处理出fa(i),fb(i),表示iAB中最早出现的位置。我们可以从大到小枚举aa越小,对b的限制越多,每个限制都形如bfb(i)这种形式,这样我们可以O(n)求出。
那么现在有三个数列,我们又要怎么做呢?也是考虑从大到小枚举aa越小,对b,c的限制越多,每个限制都形如:bfb(i)cfc(i)。以b,c为横纵坐标建平面直角坐标系,将限制映射到坐标系上,我们发现这其实就是将点(fb(i),fc(i))左下的区域全部挖去。那么此时我们怎么找最小的b+c呢?观察发现,我们挖掉不能取的那些区域后,剩下的区域有很多向左下凸出来的拐角,那么最小的b+c必定在这些拐角中的一个取得。所以我们现在的目标就是维护这个阶梯形状的东西。
考虑维护组成阶梯形的那些限制。随着a的变小,对b,c的限制不断增多,也就是不断挖掉新的区域,有时我们会发现新的区域包含了一些旧的区域,这时候旧的区域就没有必要存下来了,我们就删除这个限制。又因为一个区域最多加入一次、删除一次,一共有不超过3n个区域,用set维护的话,就可以做到O(nlogn)的复杂度了。同时再用一个multiset来维护拐角上b+c的所有可能取值即可。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
const int inf=1000000000;
int n,a[100010],b[100010],c[100010],tot;
int fa[300010]={0},fb[300010]={0},fc[300010]={0};
struct forsort
{
    int val,id1,id2;
}f[300010];
struct Pair
{
    int b,c;
};
bool operator < (Pair a,Pair b) {return a.b<b.b;}
set<Pair> s;
set<Pair>::iterator it;
multiset<int> vals;
multiset<int>::iterator valit;

bool cmp(forsort a,forsort b)
{
    return a.val<b.val;
}

void init()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&f[++tot].val);
        f[tot].id1=1,f[tot].id2=i;
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&f[++tot].val);
        f[tot].id1=2,f[tot].id2=i;
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&f[++tot].val);
        f[tot].id1=3,f[tot].id2=i;
    }

    sort(f+1,f+3*n+1,cmp);
    tot=0;
    for(int i=1;i<=3*n;i++)
    {
        if (i==1||f[i].val!=f[i-1].val) tot++;
        if (f[i].id1==1) a[f[i].id2]=tot;
        if (f[i].id1==2) b[f[i].id2]=tot;
        if (f[i].id1==3) c[f[i].id2]=tot;
    }

    for(int i=1;i<=n;i++)
        if (!fa[a[i]]) fa[a[i]]=i;
    for(int i=1;i<=n;i++)
        if (!fb[b[i]]) fb[b[i]]=i;
    for(int i=1;i<=n;i++)
        if (!fc[c[i]]) fc[c[i]]=i;
}

void insertPair(int newb,int newc)
{
    int nowb,nowc;
    Pair e={newb,newc};
    it=s.upper_bound(e);
    if ((*it).c>=e.c) return;
    nowc=(*it).c;
    it--;
    valit=vals.find((*it).b+nowc);
    vals.erase(valit);
    while((*it).c<=e.c)
    {
        nowb=(*it).b,nowc=(*it).c;
        it--;
        vals.erase((*it).b+nowc);
        it++;
        s.erase(it);
        it=s.lower_bound(e);
        it--;
    }
    nowb=(*it).b,nowc=(*it).c;
    vals.insert(nowb+e.c);
    it++;
    nowc=(*it).c;
    vals.insert(e.b+nowc);
    s.insert(e);
}

void work()
{
    Pair e;
    e.b=0,e.c=inf+1;
    s.insert(e);
    e.b=inf+1,e.c=0;
    s.insert(e);
    vals.insert(0);
    for(int i=1;i<=tot;i++)
        if (!fa[i]) insertPair(fb[i]?fb[i]:inf,fc[i]?fc[i]:inf);
    int ans=inf;
    bool flag=1;
    for(int i=n;i>=1;i--)
    {
        ans=min(ans,i+(*vals.begin()));
        if (fa[a[i]]==i)
        {
            if (!fb[a[i]]&&!fc[a[i]]) {flag=0;break;}
            insertPair(fb[a[i]]?fb[a[i]]:inf,fc[a[i]]?fc[a[i]]:inf);
        }
    }
    if (flag) ans=min(ans,(*vals.begin()));
    printf("%d",ans);
}

int main()
{
    init();
    work();

    return 0;
}
posted @ 2018-04-12 19:41  Maxwei_wzj  阅读(89)  评论(0编辑  收藏  举报