【题解】[JOISC 2021 Day4] イベント巡り 2
求字典序最小的答案,从前往后一次考虑每一位。
即枚举 \(i\) 从 \(1\) 到 \(n\) 判断当前活动加入后是否能够达到 \(k\) 的总数,如果能则加入当前的 \(i\) 。
这样我们只用在线维护当前空缺位置能够加入活动的最大值。
考虑加入活动 \(i\) 后,最多由原来的一个连续区间断开成两个连续的区间,所以我们只用快速计算区间 \([l,r]\) 中最多能放入的方案数。
由于是静态问题,考虑倍增。令 $f[i][j] $ 表示从位置 \(i\) 开始,加入 \(j\) 个区间后到达的最近位置。
初值 \(f[L_i][i]=\min\{R_i\}\) 。
转移 \(f[i][0]=\min\{f[i][0],f[i+1][0]\}\) , \(f[i][j]=\min\{f[i+1][j],f[f[i][j-1][j-1]]\}\) 。
对于空缺的位置,我们重载运算符 $< $ ,\([l_1,r_1]<[l_2,r_2]\) 当且仅当 \(r_1<l_2\) ,然后用 set
维护即可。
时间复杂度 \(\mathcal{O}(n\log n)\) 。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 200005
using namespace std;
int n,k,L[N],R[N],f[N][17],t,o[N],b[N],T,w;
int calc(int l,int r){
if(l>r)return 0;int now=0;
pre(i,t,0)if(f[l][i]<=r+1)now+=1<<i,l=f[l][i];
return now;
}
struct node{
int l,r;
node(int ll=0,int rr=0){l=ll,r=rr;}
bool operator<(const node o)const{return r<o.l;}
};set<node>s;
int main(){
scanf("%d%d",&n,&k);t=log2(k);
rep(i,1,n)scanf("%d%d",&L[i],&R[i]),o[++w]=L[i],o[++w]=R[i];
sort(o+1,o+w+1);rep(i,1,w)if(o[i]!=o[i-1])b[++T]=o[i];
rep(i,1,n)L[i]=lower_bound(b+1,b+T+1,L[i])-b,R[i]=lower_bound(b+1,b+T+1,R[i])-b;
rep(i,1,T+2)rep(j,0,t)f[i][j]=T+2;
rep(i,1,n)f[L[i]][0]=min(f[L[i]][0],R[i]);
pre(i,T,1){
f[i][0]=min(f[i][0],f[i+1][0]);
rep(j,1,t)f[i][j]=min(f[i+1][j],f[f[i][j-1]][j-1]);
}puts("No Copy");
s.insert(node(1,T));int sum=calc(1,T);
if(sum<k){printf("-1\n");return 0;}
rep(i,1,n){
if(s.find(node(L[i],R[i]-1))==s.end())continue;
node cur=*s.find(node(L[i],R[i]-1));
if(cur.l<=L[i]&&cur.r>=R[i]-1){
int dta=calc(cur.l,L[i]-1)+calc(R[i],cur.r)-calc(cur.l,cur.r);
if(dta+sum>=k-1){
printf("%d\n",i);k--;
sum+=dta;s.erase(cur);
if(cur.l<L[i])s.insert(node(cur.l,L[i]-1));
if(cur.r>=R[i])s.insert(node(R[i],cur.r));
}
}
if(!k)return 0;
}
return 0;
}