【題解】[JOISC 2021 Day4] イベント巡り 2
阿新 • • 發佈:2021-10-04
求字典序最小的答案,從前往後一次考慮每一位。
即列舉 \(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]\}\)
對於空缺的位置,我們過載運算子 $< $ ,\([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; }