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;
}