Codeforces Round #776 (Div. 3) E 二分 贪心 分类讨论 边界处理 multiset

题目

现在 Dmitry 有一个会话,他必须通过 n 次考试。会议从第 1 天开始,持续 d 天。第i次考试将在ai(1≤ai≤d)那天进行,所有ai-都是不同的。

样本,其中 n=3,d=12,a=[3,5,9]。橙色——考试日。第一次考试前,Dmitry 休息 2 天,第二次考试前休息 1 天,第三次考试前休息 3 天。
对于课程安排,Dmitry 考虑了一个特殊值 μ——所有考试的考试前休息时间中的最小值。例如,对于上图,μ=1。换句话说,对于日程安排,他精确地计算了 n 个数字——他在考试 i-1 和考试 i 之间休息了多少天(对于 i=0,在课程开始和考试 i 之间)。然后它找到 μ——这 n 个数中的最小值。

德米特里相信他可以改进会议的日程安排。他可能会要求更改一次考试的日期(更改 ai 的任意值)。帮助他更改日期,使所有ai保持不同,并且μ的值尽可能大。

例如,对于上面的时间表,Dmitry 将第二次考试移到课程的最后是最有利的。新的时间表将采用以下形式:

现在考试前的休息时间等于 [2,2,5]。所以,μ=2。
Dmitry 可以保持提议的时间表不变(如果没有办法移动一项考试以使情况有所改善)。

输入
输入数据的第一行包含一个整数 t (1≤t≤104)——输入测试用例的数量。测试用例的描述如下。

在每个案例之前的测试中都写了一个空行。

每个测试用例的第一行包含两个整数 n 和 d (2≤n≤2⋅105,1≤d≤109)——分别是考试次数和会话长度。

每个测试用例的第二行包含 n 个整数 ai (1≤ai≤d,ai<ai+1),其中第 i 个数字表示第 i 次考试的日期。

保证所有测试用例的 n 之和不超过 2⋅105。

输出
对于每个测试用例,如果 Dmitry 可以将任何一项考试转移到任意一天,则输出 μ 的最大可能值。 ai 的所有值都应保持不同。

题解思路

求最值,先考虑二分(毕竟没直接贪心出结论的本事)。
当u值过大的时候肯定转移不出合适的结果。
当过小的时候,一定存在一种方法让他转移出合适的结果。
这样我们就可以根据二分的u值来贪心出适合的转移方法,如果方法不存在就是u值过大。
怎么贪心?
只能转换一次数组。
我们先处理出能直接转移到的超级位置。
当发现这个数不符合u值,让他先转移到超级位置,并积累(只能转换一次,出现2次这样的数就是不符合答案了)
此时这个i值被转移了,为了不影响之后的计算,我们将它的位置往前面推。
如何此时没有超级位置能够转移,就考虑将自己往后面移动几位,或者将前面的数往前移动几位。
判断两个区间是否存在放的位置。
特判最后一个位置。(WA了2次)
因为最后一个数的往后移动,只需前面区间符合条件即可。

这题还有用multiset直接贪心的做法。
非常暴力
枚举每个可能需要移动的数,将所有区间放入 multiset , 这个数要么移动到最大的区间的中间,要么移动到最后面,直接处理与这个点有关的区间,将前后两点连接,再判断放哪里,暴力更新答案即可。
特判第n个点即可。

AC代码

二分解法

#include <bits/stdc++.h>
//#include <unordered_map>
//priority_queue
#define PII pair<int,int>
#define ll long long

using namespace std;

const  int  INF =  0x3f3f3f3f;
const  int  N =  200100;
int n , d ; 
int a[N] ; 
int b[N] ; 

bool che(int p )
{
    int falg = 0 ; 
    int cnt = 0 ; 
    for (int i = 0 ; i <= n + 1 ; i++ )
        b[i] = a[i] ; 
    for (int i = 1 ; i <= n ; i++ )
    {
        int st = b[i] - b[i-1] - 1 ; 
        if ( st >= 2*p + 1 )
            cnt++;
    }
    if ( d - b[n] - 1 >= p )
        cnt++;
    int fx = 0 ; 
    for (int i = 1 ; i <= n ; i++ )
    {
        int st = b[i] - b[i-1] - 1 ;
        if ( st < p )
        {
            falg++;
            if ( falg > 1 )
                return false ; 
            if (cnt)
                b[i] = b[i-1] ; 
            else
            {
                if ( i == n )
                {
                    if ( d - b[i-1] - 1 >= p )
                    {
                        fx++;
                        continue ; 
                    }
                }
                if ( b[i+1] - b[i-1] - 1 >= 2*p + 1 )
                    fx++;
                else if ( i > 1 )
                {
                    if ( b[i] - b[i-2] - 1 >= 2*p + 1 )
                        fx++; 
                    else
                        return false ; 
                }else
                    return false ; 
            }
        }
    }
    if (fx)
        cnt++;
    if ((cnt && falg == 1 )|| !falg )
        return true ; 
    else
    {
        return false ; 
    }
}
void solve()
{
    cin >> n >> d ; 
    a[n+1] = d ; 
    for (int i = 1 ; i <= n ; i++ )
        cin >> a[i] ; 
    int t1 = 0 , t2 = d - 1 ; 
    while ( t1 < t2 )
    {
        int mid = t1 + t2 + 1 >> 1 ; 
        if (che(mid))
            t1 = mid ; 
        else
            t2 = mid - 1 ; 

    }
    cout << t1 << "\n" ; 
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int T ;
    cin >> T ;
    while (T--)
        solve() ;
    return 0 ;
}

multiset 解法

#include <bits/stdc++.h>
//#include <unordered_map>
//priority_queue
#define PII pair<int,int>
#define ll long long

using namespace std;

const  int  INF =  0x3f3f3f3f;
const  int  N =  200100;
int n , d ;
int a[N] ; 
void solve()
{
    cin >> n >> d ; 
    multiset<int> s ; 
    for (int i = 1 ; i <= n ; i++ )
        cin >> a[i] ; 
    a[0] = 0 ;
    int fx = d - a[n] - 1 ; 
    for (int i = 1 ; i <= n ; i++ )
    {
        s.insert(a[i]-a[i-1]-1) ; 
    }
    int ans = 0 ; 
    for (int i = 1 ; i < n ; i++ )
    {
        s.erase(s.find(a[i] - a[i-1] - 1 )) ; 
        s.erase(s.find(a[i+1] - a[i] - 1 )) ; 
        s.insert(a[i+1] - a[i-1] - 1 ) ; 
        int t1 = *s.begin() , t2 = *s.rbegin() ; 
        ans = max(ans,min(t1,max((t2-1)/2,fx))) ; 
        s.insert(a[i] - a[i-1] - 1 ) ; 
        s.insert(a[i+1] - a[i] - 1 ) ; 
        s.erase(s.find( a[i+1] - a[i-1] - 1)) ; 
    }
    s.erase(s.find(a[n]-a[n-1]-1)) ; 
    fx = d - a[n-1] - 1 ; 
    int t1 = *s.begin() , t2 = *s.rbegin() ; 
    ans = max(ans,min(t1,max((t2-1)/2,fx))) ; 
    cout << ans << "\n" ; 
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int T ;
    cin >> T ;
    while (T--)
        solve() ;
    return 0 ;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值