• 【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;
    }
    
    
  • 相关阅读:
    Java
    Java
    Java
    Java
    Java
    Hang Gliding线段树
    Biggest Number深搜
    2021年暑假康复性训练(Codeforces Round #731 (Div. 3))全题解
    Python GUI tkinter 随机生成题目
    ModuleNotFoundError: No module named ‘exceptions‘ 情况解决
  • 原文地址:https://www.cnblogs.com/AWCXV/p/13493750.html
Copyright © 2020-2023  润新知