线段树优化建边

P6348 [PA2011]Journeys

传送门

线段树可以用来优化一些特殊的建边,特别是n方级别的建边,我们可以建一颗入树, 一颗出树,[a, b]->[c, d],可以借助建造两个虚点完成。

如图所示 左边是入树, 右边是出树, 蓝色边权为0,当我们需要从[a, b]->[c, d]建边可以这样干, 例如[1,2]->[3,4],建立两个虚点。搭建起桥梁,我们可以神奇的发现,这样建边,居然符合条件,这就是线段树的魅力(具体细节看代码吧)

#include <bits/stdc++.h>
#define ll long long
using namespace std;
template <typename T> inline void read(T& t)
{
    int f = 0, c = getchar();
    t = 0;
    while (!isdigit(c)) f |= c == '-', c = getchar();
    while (isdigit(c)) t = t * 10 + c - 48, c = getchar();
    if (f) t = -t;
}
const int N = 5e6 + 10;
int cnt, head[N];
struct edge
{
    int to, nex, w;
} e[N << 1];
inline void add(int u, int v, int w)
{
    e[++cnt].to = v;
    e[cnt].w = w;
    e[cnt].nex = head[u];
    head[u] = cnt++;
}
int n, m, s;
int tot, Ls[N], Rs[N], In, Out;
void buildIn(int &k, int l, int r)
{
    if(l == r)
    {
        k = l;
        return;
    }
    k = ++tot;
    int mid = l + r >> 1;
    buildIn(Ls[k], l, mid);
    buildIn(Rs[k], mid + 1, r);
    add(k, Ls[k], 0);
    add(k, Rs[k], 0);
}
void buildOut(int &k, int l, int r)
{
    if(l == r)
    {
        k = l;
        return;
    }
    k = ++tot;
    int mid = l + r >> 1;
    buildOut(Ls[k], l, mid);
    buildOut(Rs[k], mid + 1, r);
    add(Ls[k], k, 0);
    add(Rs[k], k, 0);
}
void addOut(int k, int l, int r, int L, int R, int idx)
{
    if(l >= L && r <= R)
    {
        add(k, idx, 0);
        return;
    }
    int mid = l + r >> 1;
    if(L <= mid) addOut(Ls[k], l, mid, L, R, idx);
    if(R > mid)  addOut(Rs[k], mid + 1, r, L, R, idx);
}
void addIn(int k, int l, int r, int L, int R, int idx)
{
    if(l >= L && r <= R)
    {
        add(idx, k, 0);
        return;
    }
    int mid = l + r >> 1;
    if(L <= mid) addIn(Ls[k], l, mid, L, R, idx);
    if(R > mid)  addIn(Rs[k], mid + 1, r, L, R, idx);
}
void insertt(int a, int b, int c, int d)
{
    int p = ++tot;
    addOut(Out, 1, n, a, b, p);
    int q = ++tot;
    addIn(In, 1, n, c, d, q);
    add(p, q, 1);
}
struct v
{
    int x;
    ll dis;
    bool operator < (const v& a) const
    {
        return dis > a.dis;
    }
};
ll dis[N];
bool vis[N];
inline void dijstra()
{
    priority_queue<v> q;
    memset(dis, 120, sizeof dis);
    memset(vis, 0, sizeof vis);
    dis[s] = 0;
    q.push({s, 0});
    while(!q.empty())
    {
        v now = q.top();
        q.pop();
        if(vis[now.x]) continue;
        vis[now.x] = 1;
        for(int i = head[now.x] ; i; i = e[i].nex)
        {
            int y = e[i].to;
            if(dis[y] > dis[now.x] + e[i].w)
            {
                dis[y] = dis[now.x] + e[i].w;
                q.push({y, dis[y]});
            }
        }
    }
}
signed main()
{
    read(n), read(m), read(s);
    tot = n;
    buildIn(In, 1, n);
    buildOut(Out, 1, n);
    int a, b, c, d;
    while(m--)
    {
        read(a);
        read(b);
        read(c);
        read(d);
        insertt(a, b, c, d);
        insertt(c, d, a, b);
    }
    dijstra();
    for(int i = 1; i <= n; ++i) printf("%lld\n", dis[i]);
    return 0;
}

Legacy

传送门
这道题降低了难度,我们只需要在,入树或者出树上自己操作自己,不需要构建p,q两点,套模板就行

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 2e6 + 10;
int cnt, head[N];
struct edge
{
    int to, nex, w;
} e[N << 1];
inline void add(int u, int v, int w)
{
    e[++cnt].to = v;
    e[cnt].w = w;
    e[cnt].nex = head[u];
    head[u] = cnt;
}
int n, m, s;
int tot, Ls[N], Rs[N], In, Out;
void buildIn(int &k, int l, int r)
{
    if(l == r)
    {
        k = l;
        return;
    }
    k = ++tot;
    int mid = l + r >> 1;
    buildIn(Ls[k], l, mid);
    buildIn(Rs[k], mid + 1, r);
    add(k, Ls[k], 0);
    add(k, Rs[k], 0);
}
void buildOut(int &k, int l, int r)
{
    if(l == r)
    {
        k = l;
        return;
    }
    k = ++tot;
    int mid = l + r >> 1;
    buildOut(Ls[k], l, mid);
    buildOut(Rs[k], mid + 1, r);
    add(Ls[k], k, 0);
    add(Rs[k], k, 0);
}
void addIn(int k, int l, int r, int L, int R, int tag, int w)
{
    if(l >= L && r <= R)
    {
        add(tag, k, w);
        return;
    }
    int mid = l + r >> 1;
    if(L <= mid) addIn(Ls[k], l, mid, L, R, tag, w);
    if(R > mid)  addIn(Rs[k], mid + 1, r, L, R, tag, w);
}
void addOut(int k, int l, int r, int L, int R, int tag, int w)
{
    if(l >= L && r <= R)
    {
        add(k, tag, w);
        return;
    }
    int mid = l + r >> 1;
    if(L <= mid) addOut(Ls[k], l, mid, L, R, tag, w);
    if(R > mid)  addOut(Rs[k], mid + 1, r, L, R, tag, w);
}
struct v
{
    int x;
    LL dis;
    bool operator < (const v& rhs) const
    {
        return dis > rhs.dis;
    }
};
LL dis[N];
bool vis[N];
inline void dijstra()
{
    priority_queue<v> q;
    memset(dis, 120, sizeof dis);
    memset(vis, 0, sizeof vis);
    dis[s] = 0;
    q.push({s, 0});
    while(!q.empty())
    {
        v now = q.top();
        q.pop();
        if(vis[now.x]) continue;
        vis[now.x] = 1;
        for(int i = head[now.x]; i; i = e[i].nex)
        {
            int y = e[i].to;
            if(dis[y] > dis[now.x] + e[i].w)
            {
                dis[y] = dis[now.x] + e[i].w;
                q.push({y, dis[y]});
            }
        }
    }
}
signed main()
{
    cin >> n >> m >> s;
    tot = n;
    buildIn(In, 1, n);
    buildOut(Out, 1, n);
    int op, l, r, u, v, w;
    while(m-- && cin >> op)
    {
        if(op == 1)
        {
            cin >> u >> v >> w;
            add(u, v, w);
        }
        else if(op == 2)
        {
            cin >> u >> l >> r >> w;
            addIn(In, 1, n, l, r, u, w);
        }
        else
        {
            cin >> u >> l >> r >> w;
            addOut(Out, 1, n, l, r, u, w);
        }
    }
    dijstra();
    for(int i = 1; i <= n; ++i)
    {
        if(dis[i] == 8680820740569200760) cout << "-1 ";
        else cout << dis[i] << " ";
    }
    return 0;
}
posted @ 2022-07-05 08:34  std&ice  阅读(174)  评论(0)    收藏  举报