题意
从n个区间中选k个,满足这k个区间的交集最长,输出这k个区间的序号,任意可行解均可
思路
官方题解是二分结果(长度),然后将区间缩短ret-1后进行判断看是否存在k个区间,他们的交集长度不为0,对于修改后的区间从左到右扫描即可,用来标记区间是否在当前考虑范围内的代码挺有意思:
for (int i = 1; i <= n; i++) {
if (r[i] - len > l[i]) {
evs.push_back(event(l[i], 1));
evs.push_back(event(r[i] - len, -1));
}
}
下面抄袭的是某个大神的思路:既然我们要求交集,那么我们可以维护这样一个队列,队列首部的元素右端点最小,队列尾部的元素左端点最大,那么就可以保证这个队列中所有区间的交集就是队首元素右端点减去队尾元素(其实也就是新加进去的元素)的左端点
是不是有一种单调队列的感觉,代码可以写的比官方题解还要简洁,反正我是没有想到
代码
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define rep(i,a,b) for(int i=a;i<b;i++)
#define debug(a) printf("a =: %d\n",a);
const int INF=0x3f3f3f3f;
const int maxn=3e5+50;
const int Mod=1000000007;
const double PI=acos(-1);
typedef long long ll;
typedef unsigned int ui;
using namespace std;
struct Node{
int l,r,id;
bool operator<(const Node &rhs) const{
return r>rhs.r;
}
};
bool cmp(const Node &x,const Node &y){
return x.l<y.l;
}
Node s[maxn];
int n,k;
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
while(~scanf("%d %d",&n,&k)){
for(int i=1;i<=n;i++){
scanf("%d %d",&s[i].l,&s[i].r);
s[i].id=i;
}
sort(s+1,s+1+n,cmp);
priority_queue<Node> qu;
int ansL,ansR,maxLen=-1;
for(int i=1;i<=n;i++){
qu.push(s[i]);
if(qu.size()>k) qu.pop();
if(qu.size()==k){
int r=qu.top().r;
int l=s[i].l;
if(r-l>maxLen){
maxLen=r-l;
ansL=l; ansR=r;
}
}
}
printf("%d\n",maxLen+1);
if(maxLen+1==0){
for(int i=1;i<=k;i++) printf("%d ",i);
}else{
vector<int> ans;
for(int i=1;i<=n;i++){
if(s[i].l<=ansL && s[i].r>=ansR && ans.size()<k){
ans.push_back(s[i].id);
}
}
sort(ans.begin(),ans.end());
for(int i:ans) printf("%d ",i);
}
puts("");
}
return 0;
}