---
# System prepended metadata

title: Codeforces 1791 G2. Teleporters (Hard Version)
tags: [codeforces, binary search]

---

# Codeforces 1791 G2. Teleporters (Hard Version)
###### tags: `codeforces` `binary search`

[題目連結](https://codeforces.com/contest/1791/my)



   


# Method Sort + Binary search
:::info
:bulb: **Concept**:
1. 將從兩邊出發的coin 由小至大排序, 只要coin總和 小於等於 初始 coin即可.
   case 1: 前面所選的位置包含從左邊出發的位置
   直接計算數量回傳即可

   case 2: 前面所選的位置**不包含**從左邊出發的位置
           表示全部都是從右邊出發.
   採用binary search的作法

2. binary search 作法
   從case 2可以得知付出最少coin都是從右邊出發的,
   但是題目要求第一次必須從左邊出發,
   因此我們透過對右邊出發所需要的coin做排序,
   然後每次假設從 左邊 index 0 ~ n-1 出發, 
   透過binary search 搜尋 從右邊出發還可以獲得多少個位置.

:::


TC: O(NlogN) SC: O(N)
完整程式碼
```cpp=
﻿#include <bits/stdc++.h>
#include <unordered_set>
#include <unordered_map>
#include <chrono>
#include <iostream>
#include <string>
using namespace std;


typedef long long int ll;
const int mod = 998244353;
#define INF 1e9
#define CLR(a) memset(a, 0, sizeof(a))
#define SET(a, val) memset(a, val, sizeof(a))

#define N 200005
ll nums[N];
pair<ll, int> left_nums[N];
pair<ll, int> right_nums[N];
ll sum_nums[N];

bool visited[N];


int binary_search(int i, ll n, ll c)
{
    int idx = right_nums[i].second;
    int l = 0;
    int r = n - 1;

    if (c < (nums[idx] + idx + 1)) {
        return 0;
    }

    c -= (nums[idx] + idx + 1);

    while (l < r) {
        int m = r - (r - l) / 2;
        ll dup = (m > i) ? right_nums[i].first : 0;
        if (c >= (sum_nums[m] - dup)) {
            l = m;
        }
        else {
            r = m - 1;
        }

    }

    return l + (i >= l);
}

int do1(ll n, ll c)
{
    int lidx = 0;
    int ridx = 0;
    bool used_left = false;
    int output = 0;

    for (int i = 1; (i <= n); i++) {
        while (visited[left_nums[lidx].second]) {
            lidx++;
        }
        while (visited[right_nums[ridx].second]) {
            ridx++;
        }

        if (c < min(left_nums[lidx].first, right_nums[ridx].first)) {
            break;
        }

        if (left_nums[lidx] <= right_nums[ridx]) {
            c -= left_nums[lidx].first;
            visited[left_nums[lidx].second] = true;
            lidx++;
            used_left = true;
        }
        else {
            c -= right_nums[ridx].first;
            visited[right_nums[ridx].second] = true;
            ridx++;
        }

        output++;
    }

    return used_left ? output : 0;
}

void solve(int round)
{
    ll n, c, cur_c;
    
    
    int output = 0;
    
    CLR(nums);
    CLR(visited);
    CLR(left_nums);
    CLR(right_nums);
    CLR(sum_nums);

    cin >> n >> c;
    
    for (int i = 0; i < n; i++) {
        cin >> nums[i];
        left_nums[i] = { nums[i] + i + 1, i };
        right_nums[i] = { nums[i] + n - i, i };
    }

    sort(left_nums, left_nums + n);
    sort(right_nums, right_nums + n);
 
    output = do1(n, c);
    if (output > 0) {
        printf("%d\n", output);
        return;
    }
    
    for (int i = 0; i < n; i++) {
        sum_nums[i + 1] = sum_nums[i] + right_nums[i].first;
    }
    for (int i = 0; i < n; i++) {
        output = max(output, binary_search(i, n, c));
    }

    printf("%d\n", output);
}
```



