【Gym - 102174G】神圣的 F2 连接着我们(线段树优化建图)

题意

两列点均为 n n n个,分为 A A A列与 B B B列,在 A , B A,B A,B中分别有 p , q p,q p,q个关键点,其中 A A A中的 q q q个关键点的每一个,都要走到 B B B p p p个关键点的其中任意一个上。
A , B A,B A,B内部的点没有任何连边, A , B A,B A,B之间的边给出的形式如下:

  • a , b , c , d , w a,b,c,d,w a,b,c,d,w
  • A A A列的 [ a , b ] [a,b] [a,b]区间中的任意一点,有连向 B B B [ c , d ] [c,d] [c,d]区间任意一个点的边,边权为 w w w
  • 边是双向的。

A A A中的关键点可以同时移动, B B B中的任意一个关键点可以放置任意多个 A A A的关键点。求 A A A中所有关键点完成移动的最长时间。
n ≤ 1 0 5 , w ≤ 1 0 9 n\le 10^5,w\le 10^9 n105,w109

题解

对于 n n n个点建立线段树,这样对于任意一段连续的区间的点,最多都只有 log ⁡ n \log n logn个节点与之对应。线段树需要建立4个,分别表示左右两边,边的方向上行下行。

  • 所有上行,下行的边权都为 0 0 0
  • 在同一侧的下行的叶子节点(外部节点)连向上行的叶子节点,边权为 0 0 0
  • 对于原图的一条单向边,把 log ⁡ n \log n logn个在左侧上行方向的节点连向一个辅助节点,边权为 w w w,该辅助节点连向右侧下行方向的节点,边权为 0 0 0。(双向边需要两个辅助节点)
  • 新建超级源点,连向右边所有的关键点,边权为 0 0 0

之后只要跑最短路,对所有的 A A A列的关键点的最短路径长度取 max ⁡ \max max就是最后的答案。

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

const int maxn = 2000010;
const ll inf = ~0llu >> 1u;

struct gra {
    int head[maxn], to[maxn << 2], nxt[maxn << 2], cnt;
    ll f[maxn << 2];
    void add(int a, int b, ll c) {
        nxt[++cnt] = head[a], to[head[a] = cnt] = b, f[cnt] = c;
    }
};

struct dij : public gra {
    struct node {
        int x;
        ll v;
        explicit node(int x = 0, ll v = 0) : x(x), v(v) {}
        bool operator<(const node &t) const {
            return v > t.v;
        }
    };
    ll dis[maxn];
    int pcnt;
    bool vis[maxn];
    void work(int S) {
        fill(dis + 1, dis + 1 + pcnt, inf);
        fill(vis + 1, vis + 1 + pcnt, false);
        priority_queue<node> q;
        dis[S] = 0, q.push(node(S, dis[S]));
        while (!q.empty()) {
            int x = q.top().x;
            q.pop();
            if (vis[x]) continue;
            vis[x] = true;
            for (int i = head[x]; i; i = nxt[i]) {
                int u = to[i];
                if (!vis[u] && dis[u] > dis[x] + f[i]) {
                    dis[u] = dis[x] + f[i];
                    q.push(node(u, dis[u]));
                }
            }
        }
    }
} dj;

struct seg {
    int pos[maxn]{}, mx;
    seg() { mx = 0; }
#define mid ((l+r)>>1)
#define lch (now<<1)
#define rch (now<<1|1)
    void build(int now, int l, int r) {
        if (l == r) {
            pos[l] = now;
            mx = max(now, mx);
            return;
        }
        build(lch, l, mid);
        build(rch, mid + 1, r);
    }
    static void get(int now, int l, int r, int p1, int p2, vector<int> &v) {
        if (l == p1 && r == p2) {
            v.push_back(now);
            return;
        }
        if (p2 <= mid) get(lch, l, mid, p1, p2, v);
        else if (p1 >= mid + 1) get(rch, mid + 1, r, p1, p2, v);
        else get(lch, l, mid, p1, mid, v), get(rch, mid + 1, r, mid + 1, p2, v);
    }
} s;

int main() {
    int n, m, p, q;
    scanf("%d%d%d%d", &n, &m, &p, &q);
    s.build(1, 1, n);
    int cn = dj.pcnt = s.mx * 2, dn = s.mx;
    for (int i = 1; i <= dn; i++) {
        for (int j = 0; j <= 1; j++) {
            int x = i * 2 + j;
            if (x <= dn) {
                dj.add(x, i, 0);
                dj.add(dn + i, dn + x, 0);
                dj.add(cn + x, cn + i, 0);
                dj.add(cn + dn + i, cn + dn + x, 0);
            }
        }
        int x = i;
        if (x * 2 + 1 > dn && x * 2 > dn) {
            dj.add(dn + x, x, 0);
            x += cn;
            dj.add(dn + x, x, 0);
        }
    }
    dj.pcnt *= 2;
    for (int i = 1; i <= m; i++) {
        int a, b, c, d, w;
        vector<int> v1, v2;
        scanf("%d%d%d%d%d", &a, &b, &c, &d, &w);
        s.get(1, 1, n, a, b, v1);
        s.get(1, 1, n, c, d, v2);
        int ins = ++dj.pcnt;
        for (auto x : v1) dj.add(x, ins, w);
        for (auto x : v2) dj.add(ins, cn + dn + x, 0);
        ins = ++dj.pcnt;
        for (auto x : v1) dj.add(ins, dn + x, 0);
        for (auto x : v2) dj.add(cn + x, ins, w);
    }
    vector<int> x, y;
    for (int i = 1, a; i <= p; i++) scanf("%d", &a), x.push_back(a);
    for (int i = 1, a; i <= q; i++) scanf("%d", &a), y.push_back(a);
    int S = ++dj.pcnt;
    for (auto t : y) dj.add(S, cn + s.pos[t], 0);
    dj.work(S);

    ll res = 0;
    for (auto t : x) res = max(res, dj.dis[s.pos[t]]);
    if(res == inf) puts("boring game");
    else printf("%lld\n", res);

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值