Торгівля#7482

Торгівля (Розбір)

Побудуємо дерево відрізків на діапазоні і спочатку обнулимо його. Нехай торговець продавав шапки в населених пунктах [l_i; r_i], починаючи з ціни p_i. Розіб'ємо відрізок [l_i; r_i] на фундаментальні відрізки [lx_1; ry_1], [lx_2; ry_2], ..., [lx_k; ry_k]. Тоді у вершину, що відповідає відрізку [lx_q; ry_q]~(1 \le q \le k), заносимо число

p_i + сума~довжин~відрізків~[lx_t; ry_t], для~всіх~t < q.

Наприклад, нехай n = 5, і перша торгівля охоплює міста з номерами від до , починаючи з ціни . Тоді відрізок розпадеться на фундаментальні: [2; 2], [3; 3], [4; 5]. Після обробки першої операції дерево відрізків буде виглядати наступним чином:

Якщо фундаментальний відрізок містить значення , це означає, що:

Розглянемо процедуру обробки запиту [left; right] (відрізка, на якому торговець веде торгівлю) на діапазоні [lpos; rpos], якщо початкова ціна шапки становить .

В лівому підвідрізку торговець буде продавати шапки в len = min(mid, right) - left + 1 містах, починаючи з ціни . В правому підвідрізку перша ціна шапки в місті, куди прийде торговець, становитиме x + len. Таким чином, на відрізку з номерами [max(left, mid + 1), right] торгівля почнеться з ціни x + len. Однак якщо len < 0 (що можливо, якщо весь відрізок запиту знаходиться в правому підвідрізку), слід покласти len = 0.

Розглянемо відрізок , у якого є два підвідрізки: і [m + 1; b]. Якщо в місті торговець продав шапку за ціною , то в місті m + 1 її ціна становитиме p + m + 1 - a.

Під час обробки запитів реалізуємо проталкивання значень, що підтримує максимум.

Для виводу відповіді слід пройти по дереву, протиснути всі значення до листя і вивести їх.

Приклад

Розглянемо виконання двох запитів, наведених у прикладі. Розіб'ємо відрізки запитів на фундаментальні:

Реалізація алгоритму

Оголосимо масив SegTree для зберігання дерева відрізків.

#define MAX 300010
long long SegTree[4*MAX];

Функція Push виконує проталкивання значення з вершини в її нащадків.

void Push(int v, int lpos, int mid, int rpos)
{
  if (SegTree[v])
  {
    SegTree[2 * v] = max(SegTree[2 * v], SegTree[v]);
    SegTree[2 * v + 1] = max(SegTree[2 * v + 1], 
                             SegTree[v] + mid - lpos + 1);
    SegTree[v] = 0;
  }
}

Функція Update виконує продаж шапок на проміжку [left; right], починаючи з ціни .

void Update(int v, int lpos, int rpos, int left, int right, 
            long long x)
{
  if (left > right) return;
  if ((lpos == left) && (rpos == right))
    SegTree[v] = max(SegTree[v], x);
  else
  {
    int mid = (lpos + rpos) / 2;
    Push(v, lpos, mid, rpos);

    int len = min(mid, right) - left + 1;
    if (len < 0) len = 0;
    Update(2 * v, lpos, mid, left, min(mid, right), x);
    Update(2 * v + 1, mid + 1, rpos, max(left, mid + 1), 
                                         right, x + len);
    }
}

Функція PrintResult проталкиває значення з внутрішніх вершин до листків і виводить підсумковий стан масиву.

void PrintResult(int v, int lpos, int rpos)
{
  if (lpos == rpos)
    printf("%lld ", SegTree[v]);
  else
  {
    int mid = (lpos + rpos) / 2;
    Push(v, lpos, mid, rpos);

    PrintResult(2 * v, lpos, mid);
    PrintResult(2 * v + 1, mid + 1, rpos);
  }
}

Основна частина програми. Читаємо вхідні дані.

scanf("%d %d", &n, &m);
memset(SegTree,0,sizeof(SegTree));

Обробляємо запитів.

for(i = 0; i < m; i++)
{
  scanf("%d %d %lld", &l, &r, &x);
  Update(1,1,n,l,r,x);
}

Виводимо результат.

PrintResult(1,1,n);
printf("\n");