P14857 [ICPC 2021 Yokohama R] Planning Railroad Discontinuation 题解

P14857 [ICPC 2021 Yokohama R] Planning Railroad Discontinuation 题解

有一张 \(l\) 层每层 \(n\) 个点的图,第 \(i\) 层有权值 \(a_i,b_i\)。有 \(m\) 种类型一和 \(r\) 种类型二的边。对于类型一的边给定 \((u,v,w)\),对每个 \(1\le i\le l\)\((i,u)\)\((i,v)\) 间有一条边权为 \(b_i+w\) 的边。对于类型二的边给定 \(x\),对每个 \(1\le i\le l\)\((i,x)\)\((i\bmod l+1,x)\) 间有一条边权为 \(a_i\) 的边。求这张图的 MST 大小。

\(n\le 10^4,m,l\le 10^5\)

整张图的点数和边数都很大,显然不能直接跑 MST,可以考虑去掉一些没用的边。根据结论,如果一条边是某个环上的最大值就可以直接去掉。如果一条边在某个子图中且没有出现在这个子图的 MST 中,则也可以去掉。于是我们可以先对所有一类边跑一遍 MST,并只保留这些边。对于二类边,相当于对每个 \(x\) 都连了一个大小为为 \(n\) 的环,找到 \(a_i\) 的最大值,这两层间的边一定没用,即可以断环成链。

接下来可以考虑 \(a_i=0\) 怎么做,模拟 Kruskal 的过程,先会连出 \(n\) 条链。设类型二中的 \(x\) 为关键点,那么在对所有一类边做 MST 时,如果一条边连接了两个包含关键点的连通块,则称这条边为关键边。可以发现每层的非关键边一定在 MST 中,而对于关键边只用连一层就够了,所以还要连上 \(b_i\) 最小那一层的所有关键边。

拓展到原问题,每层的非关键边仍然都在 MST 中,考虑一条第 \(i\) 层的关键边在 MST 中的条件,即不能有一个包含这条边的环使得环上的边权都比这条边小(边权相同的话可以定义一类边比二类边小,同类型层数小的边更小),而这个环一定由这条边、层 \(j\) 对应的边和 \(i,j\) 之间的所有二类边构成,否则一定不优,相当于看在 Kruskal 时第 \(i\) 层第一次和 \(b_j\le b_i\) 的第 \(j\) 层连通是什么时刻。

于是对于每个 \(i\),找到 \(i\) 左边第一个 \(j\) 满足 \(b_j\le b_i\),以及右边第一个 \(k\) 满足 \(b_k < b_i\),设 \([j,i]\) 间二类边边权最大值为 \(mx1\)\([i,k]\) 间为 \(mx2\),那么一条第 \(i\) 层中边权为 \(w\) 的关键边在 MST 中的条件就是 \(w+b_i\le \min(mx1,mx2)\)。预处理出所有关键边边权排序后的数组,每次二分即可。

接下来考虑二类边的贡献,此时可以直接模拟 Kruskal 的过程,根据上面的方法只保留算进答案中的一类边,求出每层的连通块数 \(s_i\),然后按 \(a_i\) 从小到大依次加边。设当前考虑连接第 \(x\)\(x+1\) 层的边,则会加入 \(\max(s_x,s_{x+1})\)\(a_x\) 的二类边,然后将这两层合并,新的连通块数为 \(\min(s_x,s_{x+1})\),直到做完 \(l-1\) 次合并即可。

\(n,m,l\) 同阶,复杂度为 \(\mathcal{O}(n\log n)\)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
using namespace std;
const int N = 1e5+5;
const int inf = 2e9;
int tp[N],f[N],A[N],B[N],a[N],b[N],n,m,l;
int id[N],st[N],mi[N],mx[N],g[N],cnt,top;
ll sg[N],ans,sum;
bool vis[N];
struct node{int u,v,w;}e[N];
int fd(int x){return x == f[x]?x:f[x] = fd(f[x]);}
char buf[1<<21],*p1,*p2;
inline int rd()
{
    char c;int f = 1;
    while(!isdigit(c = getchar()))if(c=='-')f = -1;
    int x = c-'0';
    while(isdigit(c = getchar()))x = x*10+(c^48);
    return x*f;
}
int main()
{
    // freopen("recow.in","r",stdin);
    // freopen("recow.out","w",stdout);
    n = rd();m = rd();
    for(int i = 1;i <= m;i++)e[i] = {rd()+1,rd()+1,rd()};
    l = rd();int p = 0;
    for(int i = 1;i <= l;i++)
    {
        A[i] = rd();B[i] = rd();sum += B[i];
        if(A[i] > A[p])p = i;
    }
    for(int i = 1;i <= l;i++)
    {int j = (p+i-1)%l+1;a[i] = A[j];b[i] = B[j];}
    for(int r = rd();r--;)vis[rd()+1] = 1;
    for(int i = 1;i <= n;i++)f[i] = i,tp[i] = vis[i];
    sort(e+1,e+m+1,[](node x,node y){return x.w < y.w;});
    for(int i = 1;i <= m;i++)
    {
        int x = fd(e[i].u),y = fd(e[i].v);
        if(x == y)continue;
        if(tp[x]&&tp[y])g[++cnt] = e[i].w;
        else ans += 1ll*l*e[i].w+sum;
        f[y] = x;tp[x] |= tp[y];
    }
    for(int i = 1;i <= cnt;i++)sg[i] = sg[i-1]+g[i];
    for(int i = 1;i <= l;i++)
    {
        int now = a[i-1];
        while(top&&b[i] < b[st[top]])now = max(now,mx[st[top]]),top--;
        mx[st[top]] = max(mx[st[top]],now);
        mi[i] = top?mx[st[top]]:inf;
        st[++top] = i;mx[i] = 0;
    }
    top = 0;
    for(int i = l;i;i--)
    {
        int now = a[i];
        while(top&&b[i] <= b[st[top]])now = max(now,mx[st[top]]),top--;
        mx[st[top]] = max(mx[st[top]],now);
        mi[i] = min(mi[i],top?mx[st[top]]:inf);
        st[++top] = i;mx[i] = 0;
    }
    for(int i = 1;i <= l;i++)
    {
        int j = upper_bound(g+1,g+cnt+1,mi[i]-b[i])-g-1;
        ans += sg[j]+1ll*b[i]*j;mi[i] = 1+cnt-j;
        f[i] = id[i] = i;
    }
    sort(id+1,id+l,[](int x,int y){return a[x] < a[y];});
    for(int i = 1;i < l;i++)
    {
        int x = id[i],u = fd(x),v = fd(x+1);
        ans += 1ll*max(mi[u],mi[v])*a[x];
        f[v] = u;mi[u] = min(mi[u],mi[v]);
    }
    cout << ans << endl;
    return 0;
}
posted @ 2026-01-14 17:58  max0810  阅读(10)  评论(0)    收藏  举报