【Codeforces 1389D】Segment Intersections

题目链接

点我呀

翻译

给你 \(n\) 对区间,一开始每对区间都是 [l1,r1] 和 [l2,r2]。

每对区间你都可以求他们的交集的长度,长度定义是尾巴减头部 (不用加 \(1\)),每对区间的交集和加起来,要求最后的交集的和大于等于 \(k\)

并且你可以扩展 \((x,y)\) 成为 \((x-1,y)\)\((x,y+1)\)

问你最少需要多少次这样的扩展操作才能满足要求。

题解

首先交换两个区间,让这两个区间满足 \(l1 <= l2\)

然后对 \(l2 \le r2\) (有相交部分), 和 \(l2 \gt r2\) (无相交部分$ 进行分类讨论。

具体的讨论步骤写在代码的注释里面了,主要就是贪心的过程。看看是变动新的区间,还是就在这个区间上进行扩展 (完全重合之后,就只能通过同时变动两个区间增加重叠部分了)。

代码

#include <bits/stdc++.h>
#define LL long long
using namespace std;

int T;
int n, k,l1,r1,l2,r2;

int main(){
    #ifdef LOCAL_DEFINE
        freopen("E://9.AlgorithmCompetition//Visitor.txt","r",stdin);
    #endif
    ios::sync_with_stdio(0),cin.tie(0);
    cin >> T;
    while (T--){
        cin >> n >> k;
        cin >> l1 >> r1;
        cin >> l2 >> r2;
        if (k == 0){
            cout << 0 << endl;
            continue;
        }
        if (l1 > l2){
            swap(l1,l2);
            swap(r1,r2);
        }
     	if (l2 <= r1){
     	 	if (r2 <= r1){
     	 	 	//算重叠部分
     	 	 	LL tmp = r2-l2;
                tmp*=n;
                if (tmp >= k){
                    //重叠部分就够了
                    cout << 0 << endl;
                }else{
                    //需要额外的步骤数
                    LL ans = 0;
                    //不够,那么求出需要多少
                    LL need = k-tmp;
                    //一步就能增加交集的步数
                    LL onestep = r1-r2+l2-l1;
                    if (onestep != 0){
                        //求出需要多少个区间才够
                        LL needI = need/onestep + (need%onestep!=0);
                        //但是要和 n 取较小值。
                        //如果不够
                        if (needI > n){
                            //填满 n 个
                            need -= n*onestep;
                            ans += n*onestep;
                        }else{
                            //够了
                            //直接加上 need 步,然后让need变成0
                            ans += need;
                            need = 0;
                        }
                    }
                    //剩下的都要两步才能增加一个交集
                    ans += need*2;
                    cout << ans << endl;
                }
     	 	}else{
                //r2 > r1
                //算重叠部分
                LL tmp = r1-l2;
                tmp*=n;
                if (tmp >= k){
                    cout << 0 << endl;
                }else{
                    // 需要额外步骤数
                    LL ans = 0;
                    //还需要的贡献
                    LL need = k - tmp;
                    //l2 往左或者r1往右都能操作一次就增加一个贡献
                    LL onestep = r2 - r1 + l2 - l1;
                    if (onestep != 0){
                        // 需要的区间数
                        LL needI = need / onestep + (need%onestep!=0);
                        // 若区间数不够
                        if (needI > n){
                            //填满 n 个区间
                            ans += n*onestep;
                            need -= n*onestep;
                        }else{
                            //区间数够,那么直接可以让need清零
                            ans += need;
                            need = 0;
                        }
                    }
                    //剩下的部分都只能通过加两次得到了
                    ans += need*2;
                    cout << ans << endl;
                }
     	 	}
     	}else{
            //l2 > r1
            //两个区间不相交。
            //每个区间都必然有一个操作之后不能增加相交段的过程。
            //这一段结束之后,就可以满足操作一次就增加一个相交段,直至到最大的那个区间
            //但是之后呢? 是用新的区间还是就两个两个的?
            //就两个操作增加一个区间的话,很好算。就是 rest/2 (上取整)
            //有用新的区间的情况呢?又或者,用了新的区间之后,在最后那段又两个两个加
            //利用新区间增加 x 的代价就为 (l2-r1)+x 其中括号内是常量
            //并且这是只用一对区间的情况,x ∈ [0,max(len1,len2)]。
            //那么我们可以每 max(len1,len2) 段进行考虑,如果它除 2 比 l2-r1+max(len1,len2) 来的大
            //那么这段就用新增加段的形式,否则就用两个一增的形式。
            //最后会余下来 rest%max(len1,len2) 需求量
            //这部分怎么办?
            //看起来这部分也可以用比较的方式做。。
            //就除2 和 (l2-r1)+x 比较?
            //但是要统计用了多少个区间。。。

            //不相交就没有重叠部分了。。
            LL need = k;
            //需要操作多少次才能重叠
            LL gap = l2-r1;
            //重叠之后可以最多再增加到的长度。
            LL maxl = r2-l1;
            // 如果在一个重叠区间内就能搞定,则直接输出代价。
            if (need <= maxl){
                cout << gap + need << endl;
            }else{
                //计算操作数
                LL ans = 0;
                //首先用掉一个区间
                ans += maxl+gap;
                need -= maxl;
                n--;

                //对于每个 maxl 的增加量,对比 maxl + gap 和 maxl/2
                //直接两个加一个增加量
                LL cost1 = maxl*2;
                //用新的区间
                LL cost2 = maxl + gap;
                if (cost2 < cost1){
                    //如果用新的区间更优
                    //获取需要的区间个数
                    LL needI = need/maxl;
                    //但是不能超过剩余区间个数 n
                    needI = min(needI,1LL*n);
                    //需求量满足了 needI*maxl
                    need -= needI*maxl;
                    //相应的,代价为needI个区间重叠的代价
                    ans += cost2*needI;
                    //区间数减掉needI
                    n-=needI;
                }
                //剩余还有 need
                //如果need 大于等于 maxl, 说明区间不够了, 直接用两个的
                if (need >= maxl || n == 0){
                    ans += need*2;
                }else{
                    //区间够用, 那么就看最后 need (need < maxl) 用哪个更划算
                    cost1 = need*2;
                    cost2 = need + gap;
                    //不用比较了, 直接加上较小值就好
                    ans += min(cost1,cost2);
                }
                cout << ans << endl;
            }
     	}
    }
    return 0;
}

posted @ 2020-08-12 23:28  AWCXV  阅读(170)  评论(0编辑  收藏  举报