UOJ619 【JOISC2021】活动参观 2

UOJ619 【JOISC2021】活动参观 2

倍增

昨天头痛,写了一堆毒瘤代码,只拿了\(1pts\)

今天仔细看了一看,发现有点水

我们考虑按照编号从小到大加入,相当于强行钦定一个区间必须加入,判断是否合法。

首先,我们一定是把当前区间加入一个没有被覆盖过的区间中,从而把整个区间划成\(3\)段,中间一段就是我们钦定的区间,贡献为\(1\),我们只需要计算剩下两段的贡献。

剩下两段的贡献,本质上来说,就是一个区间内最多能够包含多少个子区间。

我们可以贪心,比如从右往左加入区间,我们必然希望第一个区间的左端点尽量靠右,之后的区间同样要使左端点尽量靠右,显然这样可以得到最优解。

实际上,我们选取区间的过程是重复的,每次我们都会选取左端点尽量靠右的区间,因此我们可以倍增处理,容易得到一个区间的答案。

然后判断是否可行,可行的话就拆分区间,用\(\operatorname{set}\)维护,即可快速找到当前区间所包含的区间。

判断区间是否相交,可以直接在\(\operatorname{set}\)中查询。

\(Code:\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<set>
#include<map>
#define N 100005
#define pr pair<int,int>
#define mp make_pair
#define IT set< pr > :: iterator
using namespace std;
const int INF=1000000007;
int n,k,c0,cnt,ck[N << 1];
int a0,ans[N];
map< pr , int >H;
int st[N << 1][22];
void ckmax(int &x,int y)
{
    x=(x>y)?x:y;
}
struct seg
{
    int l,r;
    bool operator < (const seg &A) const
    {
        return r<A.r;
    }
}g[N],h[N];
int calc(int l,int r)
{
    if (l>r)
        return 0;
    if (H.find(mp(l,r))!=H.end())
        return H[mp(l,r)];
    int e(0),x(r);
    for (int i=20;i>=0;--i)
        if (st[x][i]>=l)
            e+=(1 << i),x=st[x][i];
    return H[mp(l,r)]=e;
}
set< pr >ps;
int main()
{
    scanf("%d%d",&n,&k);
    for (int i=1;i<=n;++i)
        scanf("%d%d",&g[i].l,&g[i].r),ck[++c0]=g[i].l,ck[++c0]=g[i].r;
    sort(ck+1,ck+c0+1);
    c0=unique(ck+1,ck+c0+1)-ck-1;
    for (int i=1;i<=n;++i)
    {
        g[i].l=lower_bound(ck+1,ck+c0+1,g[i].l)-ck;
        g[i].r=lower_bound(ck+1,ck+c0+1,g[i].r)-ck;
        h[i]=g[i];
    }
    sort(h+1,h+n+1);
    int o(1);
    for (int i=1;i<=c0;++i)
    {
        memcpy(st[i],st[i-1],sizeof(st[i-1]));
        while (o<=n && h[o].r==i)
        {
            int l(h[o].l);
            ckmax(st[i][0],l);
            ++o;
        }
        for (int j=1;st[i][j-1] && j<=20;++j)
            st[i][j]=st[st[i][j-1]][j-1];
    }
    cnt=calc(1,c0);
    if (cnt<k)
    {
        puts("-1");
        return 0;
    }
    for (int i=1;i<=n;++i)
    {
        int l(g[i].l),r(g[i].r);
        IT it=ps.upper_bound(mp(r-1,INF));
        int cl,cr;
        if (it==ps.end())
            cr=c0; else
            cr=it->first;
        if (it==ps.begin())
            cl=1; else
            --it,cl=g[it->second].r;
        if (cl>l)
            continue;
        int t0(calc(cl,cr)),t1(calc(cl,l)),t2(calc(r,cr));
        if (cnt-t0+t1+t2+1>=k)
        {
            ans[++a0]=i;
            cnt=cnt-t0+t1+t2+1;
            ps.insert(mp(l,i));
            if (a0==k)
                break;
        }
    }
    for (int i=1;i<=a0;++i)
        printf("%d\n",ans[i]);
    return 0;
}
posted @ 2021-05-01 14:00  GK0328  阅读(161)  评论(0编辑  收藏  举报