网上好像都是堆的做法啊......我这个不算离散化是$O(n)$的说(虽然有一坨vector可能不开O2会爆炸)
题目即是让我们求是否存在一个最长的是不少于$k$个给出区间子集的区间,如果存在输出方案。我们发现当我们的左端点固定时,右端点越向右越可能不合法,但同时答案在不断扩大(好像不太严谨),这给了我们一个更新答案的条件;而当右端点固定左端点向右移动时,答案只会越来越合法同时越来越不优。这样我们就可以尺取做了,问题是如何统计当前区间是几个区间的子集呢?
事实上这不需要任何数据结构,我们将每个区间拆成左右端点分别用vector存起来,同时开一个数组用来标记,然后当我们的指针遇到区间时讨论:
如果左指针碰到左端点,检查一下这个区间的右端点是否在右指针右边,如果在且这个区间还没被标记,则标记它并记录贡献
如果右指针扫过右端点(注意是扫过),检查一下这个区间是否被标记,如果被标记则移除它的贡献
如果右指针碰到右端点,检查一下这个区间的左端点是否在左指针左边,如果在且这个区间还没被标记,则标记它并记录贡献
这样如果不算离散化就是$O(n)$的辣
1 #include<cstdio> 2 #include<vector> 3 #include<cstring> 4 #include<utility> 5 #include<algorithm> 6 using namespace std; 7 const int N=1000005; 8 vector<pair<int,int> > ln[2*N],rn[2*N]; 9 int n,k,c,p1,p2,a1,a2,cnt,xnt,tot,ans; 10 int ll[N],rr[N],uni[2*N],mark[N]; 11 void renew(int typ) 12 { 13 if(!typ) 14 { 15 for(int i=0;i<(int)ln[p1].size();++i) 16 if(ln[p1][i].first>=p2) 17 if(!mark[ln[p1][i].second]) 18 mark[ln[p1][i].second]=true,xnt++; 19 } 20 else 21 { 22 for(int i=0;i<(int)rn[p2-1].size();i++) 23 if(mark[rn[p2-1][i].second]) 24 mark[rn[p2-1][i].second]=false,xnt--; 25 for(int i=0;i<(int)rn[p2].size();i++) 26 if(rn[p2][i].first<=p1) 27 if(!mark[rn[p2][i].second]) 28 mark[rn[p2][i].second]=true,xnt++; 29 } 30 } 31 int main () 32 { 33 scanf("%d%d",&n,&k); 34 for(int i=1;i<=n;i++) 35 { 36 scanf("%d%d",&ll[i],&rr[i]); 37 uni[++tot]=ll[i],uni[++tot]=rr[i]; 38 } 39 sort(uni+1,uni+1+tot),tot=unique(uni+1,uni+1+tot)-uni-1; 40 for(int i=1;i<=n;i++) 41 { 42 ll[i]=lower_bound(uni+1,uni+1+tot,ll[i])-uni; 43 rr[i]=lower_bound(uni+1,uni+1+tot,rr[i])-uni; 44 ln[ll[i]].push_back(make_pair(rr[i],i)); 45 rn[rr[i]].push_back(make_pair(ll[i],i)); 46 } 47 p1=p2=0; 48 while(p1<=tot&&p2<=tot) 49 { 50 if(p1>p2) p2=p1,renew(1); 51 if(xnt>=k) 52 { 53 while(xnt>=k&&p2<=tot) p2++,renew(1); 54 if(uni[p2-1]-uni[p1]>ans) ans=max(ans,uni[p2-1]-uni[p1]),a1=p1,a2=p2-1; 55 } 56 p1++,renew(0); 57 } 58 printf("%d\n",ans); 59 for(int i=1;i<=n&&c<k;i++) 60 if(ll[i]<=a1&&rr[i]>=a2) 61 printf("%d ",i),c++; 62 return 0; 63 }