P5680 [GZOI2017]共享单车 题解

【模板】读题

本蒟蒻码量上 3k 的第二题

B 公司为了减少成本,回收时从区域 K 到任何一个区域 X 都选择长度最短的路径,如果有多条到某一个区域的最短路径,则选择所有最短路径中该区域的前一区域编号最小的一条路径,称这条路径为 KX回收路线。所有的回收路线组成一棵树状结构,称之为回收路线树

根据题意,回收路线树就是最短路径树 T,跑一遍记录方案的 Dijkstra 求出即可。

B 公司每次会回收若干个区域的单车,称这些区域为回收区域B 公司还将某些区域设为投放区域,称其余区域为非投放区域。在回收路线树上,标记出区域 K ,标记出所有的回收区域,以及标记出任意两个回收区域回收路线树上的最近公共祖先。

根据题意,标记 T 上以回收区域为关键点集的虚树 V,询问时建立虚树即可。

A 公司对 B 公司的回收行动造成了阻碍,当且仅当对任意一个 K 以外的被标记的投放区域 X,从区域 KX回收路线上都存在两个被标记的区域,它们之间所有道路(回收路线树上两点路径)被阻碍。阻碍一条道路的代价为该道路的长度。

根据题意,求阻碍 V 上所有投放区域的代价,跑虚树上 DP 即可。

维护 tx 表示 x 是否投放区域,操作 0 直接把给定的点的 tx 取反即可。

以操作 1 给定的点集建立虚树,设 fx 表示阻碍 x 子树内所有投放区域的代价。

则有

fu=vsonu{w(u,v)tv=1min(w(u,v),fv)tv=0

注意清空。

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define P pair<int, int>
using namespace std;
struct G
{
    struct E
    {
        int v, w, t;
    } e[200050];
    int c, h[50050];
    void A(int u, int v, int w)
    {
        e[++c] = {v, w, h[u]};
        h[u] = c;
    }
} A, T, V;
bool t[50050], w[50050];
P r[50050];
priority_queue<P, vector<P>, greater<P>> q;
int n, m, s, O, p, a[50050], z[50050], b[50050], d[50050], f[50050][25], o[50050], W[50050], g[50050];
bool C(int x, int y) { return b[x] < b[y]; }
void D(int u)
{
    b[u] = ++p;
    for (int i = T.h[u], v; i; i = T.e[i].t)
        if (!b[v = T.e[i].v])
        {
            d[v] = d[f[v][0] = u] + 1;
            W[v] = W[u] + T.e[i].w;
            for (int j = 1; f[v][j - 1]; ++j)
                f[v][j] = f[f[v][j - 1]][j - 1];
            D(v);
        }
}
int L(int x, int y)
{
    if (d[x] < d[y])
        swap(x, y);
    while (d[x] > d[y])
        x = f[x][__lg(d[x] - d[y])];
    if (x == y)
        return x;
    for (int k = __lg(d[x]); k >= 0; --k)
        if (f[x][k] != f[y][k])
            x = f[x][k], y = f[y][k];
    return f[x][0];
}
void F(int u)
{
    o[u] = 0;
    for (int i = V.h[u], v; i; i = V.e[i].t)
        F(v = V.e[i].v), o[u] += min(V.e[i].w, t[v] ? 1 << 30 : o[v]);
}
int main()
{
    scanf("%d%d%d%d", &n, &m, &s, &O);
    for (int i = 0, u, v, w; i < m; ++i)
        scanf("%d%d%d", &u, &v, &w), A.A(u, v, w), A.A(v, u, w);
    memset(g, 0x3f, sizeof g);
    q.push({g[s] = 0, s});
    while (!q.empty())
    {
        int u = q.top().second;
        q.pop();
        if (!w[u])
        {
            w[u] = 1;
            for (int i = A.h[u], v; i; i = A.e[i].t)
                if (g[v = A.e[i].v] > g[u] + A.e[i].w || g[v] == g[u] + A.e[i].w && r[v].first > u)
                    q.push({g[v] = g[r[v].first = u] + A.e[i].w, v}), r[v].second = A.e[i].w;
        }
    }
    for (int i = 1; i <= n; ++i)
        if (r[i].first)
            T.A(r[i].first, i, r[i].second);
    D(s);
    for (int i = 0, r, k, c = 0, w = 1; i < O; ++i)
    {
        scanf("%d%d", &r, &k);
        if (r)
        {
            for (int j = 0; j < c; ++j)
                V.h[z[j]] = 0;
            V.c = c = 0;
            w = 1;
            for (int j = 0; j < k; ++j)
                scanf("%d", a + j);
            a[k++] = s;
            sort(a, a + k, C);
            z[c++] = a[0];
            for (int j = 1; j < k; ++j)
                z[c++] = L(a[j], a[j - 1]), z[c++] = a[j];
            sort(z, z + c, C);
            c = unique(z, z + c) - z;
            for (int j = 1, l; j < c; ++j)
                l = L(z[j], z[j - 1]), V.A(l, z[j], W[z[j]] - W[l]);
            for (int j = 0; j < c; ++j)
                if (t[z[j]])
                {
                    w = 0;
                    break;
                }
            if (w)
            {
                puts("-1");
                continue;
            }
            F(z[0]);
            printf("%d\n", o[z[0]]);
        }
        else
            for (int j = 0, x; j < k; ++j)
                scanf("%d", &x), t[x] ^= 1;
    }
    return 0;
}

顺便,这题题面 LATEX 和汉字之间少空格,直接复制过来审核没过

posted @   Jijidawang  阅读(3)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示