题解/算法 {D. Survey in Class}

题解/算法 {D. Survey in Class}

LINK: https://codeforces.com/contest/1834/problem/D;

假如最终的答案 最小值是a区间 最大值是b区间:
. 对于既不在a区间 又不在b区间的点, 如果选择该点 会导致他俩的值 都-=1 整体答案不变; (因此, 这类点 可以不选择);
因此, 我们只用关注 ab区间里的点; 令a区间长度为LA, b区间为LB, 他俩重叠区域长度为LL;
. 重叠的点 可以不选;
. 答案是: 要么选择LA - LL这个区间 (答案是2 * (LA - LL)), 要么选择LB - LL;

问题转换:
对于任意区间x 其长度为LX, 令C( x, i)表示 x区间与i区间的重叠区域的长度, 我们需要得到mi = min( C( x, ...)), 然后用2 * ( LX - mi)去更新答案;
. 核心在于求解mi;

算法:
首先对所有区间按照pair规则 排序;
此时对于任意区间x [xl, xr], 令区间t [tl, tr]是和区间x重叠区域最小的区间 令mi是他俩的重叠区域的长度, 分析t的情况: (令pre_rig为 所有x前面的区间的r端点的最小值, 令nex_lef为 所有x后面的区间的l端点的最大值);
1: t排序是在x的前面; , 则pre_rig一定是t的右端点 (这需要贪心证明), 即mi = max( pre_rig - xl + 1, 0);
2: [[t排序是在x的后面]]&&[[tl > xr]] (不相交), 则nex_lef为t的左端点;
3: [[t排序是在x的后面]]&&[[tl <= xr && tr > xr]] (即相交但不被x所包含); 则nex_lef为t的左端点 (这需要贪心证明);
4: [[t排序是在x的后面]]&&[[tr <= xr]] (即t被x完全包含);
. 这是我们讨论的核心 也是难点;
. 如果你用暴力, 即遍历他后面的所有区间j 则终止条件 不是jr > xr, 而是jl > xr; 举个例子 x: [1,3]; [2,5]; [3,3], 有一个完全被包含的区间[3,3], 但是 在[2,5]时 就break掉了, 这是不对的; 当然这会超时;
. 求解他 确实很难… 很需要思维跳跃… 直接给答案: 此时的t 一定是所有区间里 长度最小的;
. 证明: 假如有另一个区间m 其长度比t还小, 分析m的情况:
1: m排序在x的前面; 那么 m与x的重叠区域 一定小于mi, 这与假设矛盾;
2: [[m排序在x的后面]]&&[[与x不相交]]; 因为0 < mi, 这与假设矛盾;
3: [[m排序在x的后面]]&&[[与x相交但不被x包含]]; 因为m与x的重叠区域长度 < mi, 这与假设矛盾;
因此, 只剩下最后一种情况, 即 m一定属于: [[m排序在x的后面]]&&[[被x包含]]的情况; 这种情况 重叠区域长度 就等于m区间长度;
. 当然, 你还要证明下: 即使答案不来自于m(即第4种情况) 你使用m来更新答案 是不会影响答案的;
. . 简单证明下: (1: 要么m=x 此时重叠区域达到了最大值 即x的长度 这是最劣的情况 任何区间与x的重叠区域 一定是<= x长度的;) (2: 否则 m != x (m的长度 一定小于x) 此时我们以重叠区域为m来更新答案 而实际上 m区间 属于{1/2/3}情况 因此实际上m与x的重叠区域 一定是<= m的长度);

mi_len = `所有区间长度的最小值`;
sort( A, A + N);
int pre_rig = 1e9, nex_lef = A[ N-1].first;
for( int i = 0; i < N; ++i){
    auto l = A[i].first, r = A[i].second;
    auto s = r - l + 1;

    int intersect = s;
    //> pre
    Tools::Min_self( intersect, pre_rig - l + 1);
    //> nex
    Tools::Min_self( intersect, r - nex_lef + 1);
    //> mid
    Tools::Min_self( intersect, mi_len);
    Tools::Max_self( intersect, 0);

    Tools::Max_self( ANS, 2 * ( s - intersect));
    Tools::Min_self( pre_rig, r);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值