Educational Codeforces Round 75 (Rated for Div. 2) D

题意

n n 个区间,从每个区间取一个数,要求最后中位数尽可能大

题意

第一反应就是二分
但是我,二分单调性的理解,很差
我就否决了,去想枚举,然后我就没了。

实际上就是二分中位数,对于一个中位数,肯定有一部分在你左边,一部分在你右边。
如果左边部分超过一半了,说明这个中位数过大了。
如果右边部分超过一半了,说明这个中位数过小了。
如果正常情况下,剩下的只剩 l m i d r l\leq mid \leq r 的区间了,这个时候就先取最小的左端点使得左边的取满一半,然后使右边取满一半+1,右边的贡献就是中位数大小
如果这个和比 s s 小,说明符合情况,再取更大的中位数。
反之,不符合情况,取更小的中位数。

在保证左右两边都不超过的情况,你如何证明单调性呢?
首先贡献肯定是绝大部分取左端点,少部分取中位数。
大概是这样:
a l r b a \quad \quad l \quad r \quad \quad b
a a b b 保证在左边和右边的部分,首先他们不会超限,并且他们一定取左端点。
l l r r 部分就是包含中位数的部分,左边一部分取左端点,右边取中位数。
所以对原区间排序,前一半一定取左端点。
我们考虑增大中位数,显然如果先决条件成立,随着中位数增大,能够选择的区间会越多,也就是要变成中位数的越多。
s u f suf 表示后缀左坐标之和
A = m i d ( i n 2 ) + s u f [ i + 1 ] A=mid*(i-\frac{n}{2})+suf[i+1]
B = ( m i d + x ) ( i + y n 2 ) + s u f [ i + y ] B=(mid+x)*(i+y-\frac{n}{2})+suf[i+y]
比较可以发现,左部分是增大的,右部分是缩小的。
但是右部分减去的左端点,都在左部分变成了 m i d mid ,而 m i d mid 肯定是大于等于左端点的。
并且 m i d mid 还变成了 m i d + x mid+x ,所以 B A B \geq A 。满足单调性。

总结

这是我遇到的或者说第一次带合法的二分。
准确来说对于不合法情况,显然满足二分,左边多了说明大了,右边多了说明小了。
在合法情况下,也满足二分,因为左边部分一定取左端点,右边部分随着中位数变大,取中位数变多了,虽然说取左坐标的减少了,但那时因为他们都去取中位数了,所以贡献一定变大。

#include<bits/stdc++.h>
#define FOR(i,l,r) for(int i=l;i<=r;i++)
#define sf(x) scanf("%d",&x)
typedef long long ll;
using namespace std;

const ll mod = 1e9+7;
const int maxn = 2e5+100;

struct node{
    int l,r;
    friend bool operator < (node a,node b){
        if(a.l==b.l)return a.r<b.r;
        return a.l<b.l;
    }
}A[maxn];

int n;ll s;

bool check(int x){
    int lt=0,rt=0;
    ll ret=0;
    //cout<<x<<endl;
    for(int i=1;i<=n;i++){
        if(A[i].r<x)lt++,ret+=A[i].l;
        else if(A[i].l>x)rt++,ret+=A[i].l;
    }
    if(rt>=n/2+1)return true;
    if(lt>=n/2+1)return false;
    for(int i=1;i<=n;i++){
        if(A[i].r<x||A[i].l>x)continue;
        if(lt<n/2)ret+=A[i].l,lt++;
        else ret+=x,rt++;
    }
    return ret<=s;
}

int main(){
    int T;cin>>T;
    while(T--){
        scanf("%d%lld",&n,&s);
        FOR(i,1,n)scanf("%d%d",&A[i].l,&A[i].r);
        sort(A+1,A+1+n);
        int l=1,r=1e9+1,ans=0;
        while(l<=r){
            int mid=(l+r)>>1;
            if(check(mid)){
                ans=max(ans,mid);
                l=mid+1;
            }
            else r=mid-1;
        }
        printf("%d\n",ans);
    }
}

发布了203 篇原创文章 · 获赞 17 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/mxYlulu/article/details/104246811