Loading

【题解】[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;
}
posted @ 2021-10-04 17:09  7KByte  阅读(56)  评论(0编辑  收藏  举报