首页 > 其他分享 >F - Takahashi in Narrow Road

F - Takahashi in Narrow Road

时间:2024-09-24 16:26:53浏览次数:1  
标签:east leq int moves person steps Narrow Road Takahashi

F - Takahashi in Narrow Road

Problem Statement

There is a road extending east and west, and $N$ persons are on the road. The road extends infinitely long to the east and west from a point called the origin.

The $i$-th person $(1\leq i\leq N)$ is initially at a position $X_i$ meters east from the origin.

The persons can move along the road to the east or west. Specifically, they can perform the following movement any number of times.

  • Choose one person. If there is no other person at the destination, move the chosen person $1$ meter east or west.

They have $Q$ tasks in total, and the $i$-th task $(1\leq i\leq Q)$ is as follows.

  • The $T_i$-th person arrives at coordinate $G_i$.

Find the minimum total number of movements required to complete all $Q$ tasks in order.

Constraints

  • $1\leq N\leq2\times10^5$
  • $0\leq X_1 < X_2 < \dotsb < X_N \leq10^8$
  • $1\leq Q\leq2\times10^5$
  • $1\leq T_i\leq N\ (1\leq i\leq Q)$
  • $0\leq G_i\leq10^8\ (1\leq i\leq Q)$
  • All input values are integers.

Input

The input is given from Standard Input in the following format:

$N$
$X_1$ $X_2$ $\ldots$ $X_N$
$Q$
$T_1$ $G_1$
$T_2$ $G_2$
$\vdots$
$T_Q$ $G_Q$

Output

Print the answer.


Sample Input 1

5
10 20 30 40 50
4
3 45
4 20
1 35
2 60

Sample Output 1

239

An optimal sequence of movements for the persons is as follows (the positions of the persons are not necessarily drawn to scale):

For each task, the persons move as follows.

  • The 4th person moves $6$ steps east, and the 3rd person moves $15$ steps east.
  • The 2nd person moves $2$ steps west, the 3rd person moves $26$ steps west, and the 4th person moves $26$ steps west.
  • The 4th person moves $18$ steps east, the 3rd person moves $18$ steps east, the 2nd person moves $18$ steps east, and the 1st person moves $25$ steps east.
  • The 5th person moves $13$ steps east, the 4th person moves $24$ steps east, the 3rd person moves $24$ steps east, and the 2nd person moves $24$ steps east.

The total number of movements is $21+54+79+85=239$.

You cannot complete all tasks with a total movement count of $238$ or less, so print 239.


Sample Input 2

8
0 1 2 3 4 5 6 100000000
6
1 100000000
8 0
1 100000000
8 4
1 100000000
5 21006578

Sample Output 2

4294967297

Note that some persons may need to move to the west of the origin or more than $10^8$ meters to the east of it.

Also, note that the answer may exceed $2^{32}$.


Sample Input 3

12
1558 3536 3755 3881 4042 4657 5062 7558 7721 8330 8542 9845
8
9 1694
7 3296
12 5299
5 5195
5 5871
1 2491
8 1149
8 2996

Sample Output 3

89644

 

解题思路

  官方题解看不懂,只能按自己比较复杂的思路去做,结果写代码加调试用了差不多 4 个小时。提示,本篇题解会用到支持区间赋值与区间加等差数列的线段树。区间赋值的内容可以参考:扶苏的问题。区间加等差数列的内容可以参考:E. Space Harbour

  其实只要按照题意去模拟就能得到答案,难的地方就在于,每次将一个人移到目标位置时哪些人的位置也会受到影响?如何去维护这个过程中受影响的人的位置?用 $p_t$ 表示第 $t$ 给人的位置,下面只讨论 $p_t < g$ 的情况,也就是第 $t$ 个人要往右移动到目标位置。处理向左移动情况的思想是一样的,类比一下就好了。

  首先要解决第一个问题,第 $t$ 个人从 $p_t$ 移动到 $g$ 的过程中右边有多少个人也会移动。容易知道这些人的下标必定是连续的一段,我们不妨假设有 $c$ 个人(包括第 $t$ 个人),那么最后的结果就是 $\displaylines{\begin{cases} p_t \gets g \\ p_{t+1} \gets g+1 \\ \vdots \\ p_{t+i} \gets g + i - 1 \\ \vdots \\ p_{t + c - 1} \gets g + c - 1 \end{cases}}$。可以用二分找到这个 $c$,这是因为受影响的下标是连续的一段,因此具有二段性。对于二分点 $m$,check 的时候检查 $p_{t+m-1}$ 是否小于等于 $g+m-1$(等于 $g-m+1$ 的时候并不会影响答案),如果是的话呢,说明第 $t+m-1$ 个人会受到影响,否则不会受到影响。

  现在我们找到一段会受到影响的连续位置了,那么这个移动过程中对答案的贡献就是

$$(g - p_t) + (g+1 - p_{t+1}) + \cdots + (g+c-1 - p_{t+c-1}) = \frac{c \cdot (g + g-c+1)}{2} - \sum\limits_{i=t}^{t+c-1}{p_{i}}$$

  所以现在的问题是我们该如何快速求出 $\sum\limits_{i=t}^{t+c-1}{p_{i}}$,以及更新 $p_i$。

  显然我们可以用线段树去维护序列 $p_i$,那么 $\sum\limits_{i=t}^{t+c-1}{p_{i}}$ 就是简单的区间查询。而更新就很复杂了,由于我们不可能一个一个去修改,因此首先需要将 $p_t \ldots p_{t+c-1}$ 先赋值为 $g$,然后再加上一个首相为 $0$ 公差为 $1$ 的等差数列。所以实际上我们需要维护的是 $p_i$ 的差分数组,定义差分数组 $f_i = p_i - p_{i-1}$。

  与一般的支持区间加等差数列的线段树维护的东西一样,唯一不同的地方在于懒标记。除了区间加的懒标记 add 外,还需要维护区间赋值的懒标记 tag。其中当 tag 是 $\infty$ 时表示没有区间赋值的操作,不用下传。

  为了简化操作这里将区间赋值和区间加等差数列统一成一个操作,表示成函数 add(int l, int r, int c, int k, int d),意思是对区间 $[l,r]$ 内的元素先赋值为 $c$,再加上首相为 $k$ 公差为 $d$ 的等差数列,函数内容为下:

void add(int l, int r, int c, int k, int d) {
    if (c != INF) {
        modify(1, r + 1, r + 1, query(r + 1, r + 1) - c, 0);
        modify(1, l, l, c - query(l - 1, l - 1), 0);
        modify(1, l + 1, r, 0, 0);
    }
    else {
        modify(1, l, l, c, k);
        modify(1, l + 1, r, c, d);
        modify(1, r + 1, r + 1, c, -(k + (r - l) * d));
    }
}

  这部分是对上面函数的解释。规定当 $c \ne \infty$ 表示需要进行赋值操作,由于线段树维护的是差分数组,我们要考虑赋值对差分数组的影响是怎样的。对 $p_l \sim p_r$ 进行赋值,$f_l \sim f_{r+1}$ 会受到影响。首先 $p_l$ 被修改成 $c$,自然有 $f_l \gets c - p_{l-1}$,其中 query(l,r) 得到的是 $\sum\limits_{i=l}^{r}{p_i}$,所以我们需要将 $f_l$ 赋值成 $c - p_{l-1}$,modify(int u, int l, int r, LL c, LL d) 的含义就是将区间 $[l,r]$ 内的元素先赋值为 $c$ 后再加上 $d$。然后是 $f_{l+1} \sim f_{r}$,由于 $p_{l} \sim p_{r}$ 的值都是 $c$,因此需要将 $f_{l+1} \sim f_{r}$ 都赋值为 $0$。最后是需要将 $f_{r+1}$ 赋值成 $p_{r+1} - c$。代码中的顺序会不一样是为了防止修改操作对查询的影响。

  然后就是对区间 $[l,r]$ 加上等差数列了,与一般的支持区间加等差数列一样这里就不过多赘述了,包括 modify 的具体内容和懒标记的更新也与一般的支持区间赋值的线段树一样。

  所以对于一个过程,二分出 $c$ 后,需要先后调用 add(t, t + c - 1, g, 0, 0) 和 add(t, t + c - 1, INF, 0, 1) 来维护受影响的人的位置。

  AC 代码如下,时间复杂度为 $O(q\log^{2}{n})$:

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

typedef long long LL;

const int N = 2e5 + 5, INF = 0x3f3f3f3f;

int a[N];
struct Node {
    int l, r;
    LL s1, s2, tag, add;
}tr[N * 4];

void pushup(int u) {
    tr[u].s1 = tr[u << 1].s1 + tr[u << 1 | 1].s1;
    tr[u].s2 = tr[u << 1].s2 + tr[u << 1 | 1].s2;
}

void build(int u, int l, int r) {
    tr[u] = {l, r, 0, 0, INF, 0};
    if (l == r) {
        tr[u].s1 = a[l] - a[l - 1];
        tr[u].s2 = (l - 1) * tr[u].s1;
    }
    else {
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }
}

void upd(int u, LL c, LL d) { // 将区间[l,r]内的元素赋值为c后再对每个元素加上d
    // 如果c=INF意味着没有赋值操作,否则需要将区间内的元素均赋值为c
    if (c != INF) {
        tr[u].s1 = (tr[u].r - tr[u].l + 1ll) * c;
        tr[u].s2 = (tr[u].r - tr[u].l + 1ll) * (tr[u].l + tr[u].r - 2) / 2 * c;
        tr[u].tag = c;
        tr[u].add = 0;
    }
    // 再对区间内的元素均加上d
    tr[u].s1 += (tr[u].r - tr[u].l + 1) * d;
    tr[u].s2 += (tr[u].r - tr[u].l + 1ll) * (tr[u].l + tr[u].r - 2) / 2 * d;
    tr[u].add += d;
}

void pushdown(int u) {
    upd(u << 1, tr[u].tag, tr[u].add);
    upd(u << 1 | 1, tr[u].tag, tr[u].add);
    tr[u].tag = INF, tr[u].add = 0;
}

void modify(int u, int l, int r, LL c, LL d) {
    if (l > r) return;
    if (tr[u].l >= l && tr[u].r <= r) {
        upd(u, c, d);
    }
    else {
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid) modify(u << 1, l, r, c, d);
        if (r >= mid + 1) modify(u << 1 | 1, l, r, c, d);
        pushup(u);
    }
}

LL query(int u, int l, int r) { // 查询差分数组在区间[l,r]的和,一般l=1,则query(u,1,r)的结果为第r个元素的值
    if (l > r) return 0;
    if (tr[u].l >= l && tr[u].r <= r) return r * tr[u].s1 - tr[u].s2;
    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    if (r <= mid) return query(u << 1, l, r);
    if (l >= mid + 1) return query(u << 1 | 1, l, r);
    return query(u << 1, l, r) + query(u << 1 | 1, l, r);
}

LL query(int l, int r) { // 查询原数组在区间[l,r]的元素和
    return query(1, 1, r) - query(1, 1, l - 1);
}

void add(int l, int r, int c, int k, int d) { // 将区间[l,r]内的元素赋值为c后再对该区间加上首相为k公差为d的等差数列
    if (c != INF) {
        modify(1, r + 1, r + 1, query(r + 1, r + 1) - c, 0);
        modify(1, l, l, c - query(l - 1, l - 1), 0);
        modify(1, l + 1, r, 0, 0);
    }
    else {
        modify(1, l, l, c, k);
        modify(1, l + 1, r, c, d);
        modify(1, r + 1, r + 1, c, -(k + (r - l) * d));
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    build(1, 1, n + 1);
    cin >> m;
    LL ret = 0;
    while (m--) {
        int x, y;
        cin >> x >> y;
        if (query(x, x) < y) {
            int l = 1, r = n - x + 1;
            while (l < r) {
                int mid = l + r + 1 >> 1;
                if (y + mid - 1 >= query(x + mid - 1, x + mid - 1)) l = mid;
                else r = mid - 1;
            }
            ret += l * (y + y + l - 1ll) / 2 - query(x, x + l - 1);
            add(x, x + l - 1, y, 0, 0);
            add(x, x + l - 1, INF, 0, 1);
        }
        else {
            int l = 1, r = x;
            while (l < r) {
                int mid = l + r + 1 >> 1;
                if (y - mid + 1 <= query(x - mid + 1, x - mid + 1)) l = mid;
                else r = mid - 1;
            }
            ret += query(x - l + 1, x) - l * (y - l + 1ll + y) / 2;
            add(x - l + 1, x, y - l + 1, 0, 0);
            add(x - l + 1, x, INF, 0, 1);
        }
    }
    cout << ret;
    
    return 0;
}

 

参考资料

  Editorial - AtCoder Beginner Contest 371:https://atcoder.jp/contests/abc371/editorial/10931

标签:east,leq,int,moves,person,steps,Narrow,Road,Takahashi
From: https://www.cnblogs.com/onlyblues/p/18429331

相关文章

  • [ABC371F] Takahashi in Narrow Road 题解
    洛谷题目链接Atcoder题目链接前言这道题我赛时想到了正解并打了出来,然后因为把\(l\)打成了\(1\)导致WA\(\times23\),然后又没场切六道题(悲。然后赛后有人说正解是珂朵莉树/线段树,然而我是珂朵莉树\(+\)线段树,呵。题意数轴上有\(n\)个人,第\(i\)个人初始在\(X_i\)处,每一次操作......
  • 题解:AT_abc372_f [ABC372F] Teleporting Takahashi 2
    题意给出一个\(n\)个点的有向图,点\(i\)连向点\((i+1)\),点\(n\)连向点\(1\)。再给你\(m\)条额外边。你的初始位置为\(1\),问你移动\(k\)步的不同方案数(仅当路径不同时两个方案不同)。思路先想怎样暴力转移,显然移动\(k\)步到达一个点的方案数为所有跟这个点连边的移......
  • Laravel BroadcastAs 中的 Pusher 传递参数
    一、BroadcastAs简介的作用是Laravel框架中的一个特性,用于在广播事件时指定事件的名称。它的作用是提供一种更具可读性和可维护性的方式来标识广播事件。通过使用BroadcastAs,开发人员可以更清晰地表达事件的含义,使得代码更易于理解和维护。此外,BroadcastAs还可以用于在不同的......
  • 5G Multicast/Broadcast Services(MBS) (三)Broadcast
    这篇是Broadcast的overview,正文开始。值得注意的是,对于5MBSbroadcast,UE处于RRCidle/RRC connected/Inactive时,网络侧都可以通过MRB将MBS广播数据传输到UE。对于Broadcast涉及的RNTI有G-RNTI以及MCCH-RNTI。1SessionManagement对于特定服务,将执行以下阶段:(1)MBS......
  • Road of the King 题解
    RoadoftheKing题解形成强连通图的必要条件是点\(1\)能在环中,于是考虑\(1\)号点形成的强连通分量\(x\)。这类图论计数题目往往考虑dp,于是我们设\(dp_{i,j,k}\)表示走了\(i\)步,经过了\(j\)个点,\(1\)号点形成的强连通分量\(x\)的大小为\(k\)时的方案数。转移......
  • [ABC250Ex] Trespassing Takahashi
    感觉是很厉害的结论题。题意给你一个带权无向连通简单图\(G=(V,E),|V|=n,|E|=m\)。钦定编号\(1\simk\)的点为关键点。给定\(q\)次询问,每次询问给出\(x,y,t\),表示你需要回答是否存在一条路径,使得从\(x\)出发到\(y\)的路径上相邻两个关键点的距离都不超过\(t\)。保证......
  • GB28181规范中broadcast和talk模式实际场景时间差别在哪里?
    好多开发者对GB28181规范里面,broadcast和talk模式区分不清,今天借此机会,针对GB28181标准中的Broadcast(广播)和Talk(对讲)是两种不同的通信模式,它们在视频监控系统中扮演着不同的角色,做个基础的扫盲,二者具有以下区别:1.功能和用途Broadcast(广播): 功能:主要用于平台侧向设备侧发送单向的通......
  • 【VMware by Broadcom】Fusion 产品下载汇总
    Fusion产品版本百度网盘VMware-Fusion-1.0.0-51348.dmg链接:https://pan.baidu.com/s/1C8Qkr6nwV5rKrhpsv2JJ_A?pwd=t0kjVMware-Fusion-1.1.0-62573.dmgVMware-Fusion-1.1.1-72241.dmgVMware-Fusion-1.1.2-87978.dmgVMware-Fusion-1.1.3-94249.dmgVMware-F......
  • BroadcastReceiver 广播-Android四大组件 一文精讲
    目录1.广播用途与机制1.1什么时候用broadcast?1.2原理图解2.注册广播2.1静态注册2.2动态注册2.3二者区别与联系同:异:3.接受广播3.1接收系统广播3.2接收自定义附带值广播4.发送自定义广播4.1发送无序广播4.2发送有序广播4.3发送应用程序内部广播1.广播用途与......
  • CF773D Perishable Roads
    思路:注意到答案应该是链加上一串贡献相同的树的贡献,因为若\(a\tou\)的贡献比\(b\tou\)的贡献小,那么可以连\(b\toa\),答案会更优。那么有一个贪心思路,对于每个根,找到连向这个根的最短边,然后对于这条边的另一个端点,也找到连向这个端点的最短边,以此类推;很显然,这个假了。......