thucampday3 K. Conference

题意:坐标轴上有n条线段,线段认为是不相交的当且仅当 r i < l j r_i<l_j ri<lj或者 r j < l i r_j<l_i rj<li。假设现在最多不相交的线段为 m m m,题目保证n,m为偶数,让你保留 n 2 \frac{n}{2} 2n条线段,使得最多不相交的线段为 m 2 \frac{m}{2} 2m

很有意思的一道题。当时在考场上有很多人都通过了这道题,队友自告奋勇的挑战,并认为这么多人过肯定是随机算法,于是用随机算法狂交几十发,很明显过不了。其实我当时也看了一下这道题,但没有仔细地思考,没有感觉到随机算法是不对的,这说明我的直觉还是差了一些。

首先考虑怎么求最多的不相交的线段,很明显只要把所有线段的右端点排序,然后能插进去就插进去。

然后我想的考虑把某 m 2 \frac{m}{2} 2m线段看成这个最长的序列,然后找到能够不使得这个数增加的线段。

我的直觉告诉我这些线段一定是连在一起的,然后我就想处理出每个线段之后的下一条线段,然后就会连成很多棵树,然后枚举开头找连续 m / 2 m/2 m/2个最多能多选多少个。

但是总感觉哪里不对劲,因为连续的不一定是最多的,如果我这个方法正确那么直接在原来的 m m m个里面找(不用建树)应该也是对的,然后我就想到题目并不要求找最多的,只需要找到能够达到 n / 2 n/2 n/2的就行。也就是外面的线段刚好保留一半,然后我就立即意识到应该只用在原来的m个里面保留前一半或者后一半,仔细思考以后发现这是正确的,因为我们只用考虑外面线段左端点的坐落在那一半里面,任何一条外面的线段属于哪一半都是可以唯一确定的,因此这样肯定是正确的。

#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x;i<=y;i++)
#define dwn(i,x,y) for(int i=x;i>=y;i--)
#define ll long long
using namespace std;
template<typename T>inline void qr(T &x){
    x=0;int f=0;char s=getchar();
    while(!isdigit(s))f|=s=='-',s=getchar();
    while(isdigit(s))x=x*10+s-48,s=getchar();
    x=f?-x:x;
}
int cc=0,buf[31];
template<typename T>inline void qw(T x){
    if(x<0)putchar('-'),x=-x;
    do{buf[++cc]=int(x%10);x/=10;}while(x);
    while(cc)putchar(buf[cc--]+'0');
}
const int N=1e5+10;
int n,m,cnt,s[N<<1];
struct node{
    int x,y,id;
}a[N],b[N];bool v[N];
bool cmp(node p1,node p2){
    return p1.y<p2.y;
}
void solve(){
    qr(n);
    cnt=0;
    rep(i,1,n){
        qr(a[i].x),qr(a[i].y);
        s[++cnt]=a[i].x;
        s[++cnt]=a[i].y;
        v[i]=0;
    }
    sort(s+1,s+cnt+1);
    cnt=unique(s+1,s+cnt+1)-s-1;
    rep(i,1,n){
        a[i].x=lower_bound(s+1,s+cnt+1,a[i].x)-s;
        a[i].y=lower_bound(s+1,s+cnt+1,a[i].y)-s;
        a[i].id=i;
    }
    sort(a+1,a+n+1,cmp);
    b[m=1]=a[1];v[1]=1;
    rep(i,2,n){
        if(b[m].y<a[i].x){
            b[++m]=a[i];
            v[i]=1;
        }
    }
    int t=0;
    rep(i,1,n)if(!v[i]){
        if(a[i].x<=b[m/2].y)t++;
    }
    if(m/2+t>=n/2){
        rep(i,1,m/2)qw(b[i].id),putchar(' ');
        int res=n/2-m/2;
        rep(i,1,n)if(!v[i]){
            if(!res)break;
            if(a[i].x<=b[m/2].y){
                qw(a[i].id),putchar(' ');
                res--;
            }
        }
        puts("");
    }
    else{
        rep(i,m/2+1,m)qw(b[i].id),putchar(' ');
        int res=n/2-m/2;
        rep(i,1,n)if(!v[i]){
            if(!res)break;
            if(a[i].x>b[m/2].y){
                qw(a[i].id),putchar(' ');
                res--;
            }
        }
        puts("");
    }
}
int main(){
    int tt;qr(tt);
    while(tt--)solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值