BZOJ4912 [Sdoi2017]天才黑客 【虚树 + 最短路】

题目链接

BZOJ4912

题解

转移的代价是存在于边和边之间的
所以把边看做点,跑最短路
但是这样做需要把同一个点的所有入边和所有出边之间连边
\(O(m^2)\)的连边无法接受
需要优化建图

膜一下Claris的方法
对每个点,取出其入边出边,按在字典树上的\(dfs\)序排序
\(dfs\)序排序,实际上就是将字符串排序了
按照后缀数组的理论,两点之间的\(lcp\)就是两点之间相邻\(lcp\),也就是\(height\)数组的最小值
对于一个位置的\(height\),两边的点之间联通所需最小代价不超过\(height\)
所以可以用\(lca\)求出\(height\)数组,建立前缀后缀虚点
以前缀为例,横向边为\(0\),纵向边为\(height\)的大小
1318028-20180711182848155-257648352.png
这样如果想从左边的点到达右边的点,就只需经过中间最小的\(height\)
类似可以建立后缀点处理从右到左的情况

复杂度\(O(mlogm)\)

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<cmath>
#include<map>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define cls(s,v) memset(s,v,sizeof(s))
#define mp(a,b) make_pair<int,int>(a,b)
#define cp pair<int,int>
using namespace std;
const int maxn = 500005,maxm = 2000005,INF = 2000000000;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == '-') flag = 0; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 1) + (out << 3) + c - 48; c = getchar();}
    return flag ? out : -out;
}
struct EDGE{int to,nxt,w;}ed[maxm];
struct Trie{
    int h[maxn],ne,dfn[maxn],fa[maxn][16],dep[maxn],cnt,n;
    EDGE ed[maxm];
    void init(){REP(i,n) h[i] = 0; ne = 1; cnt = 0;}
    void build(int u,int v){ed[++ne] = (EDGE){v,h[u]}; h[u] = ne;}
    void dfs(int u){
        dfn[u] = ++cnt;
        REP(i,15) fa[u][i] = fa[fa[u][i - 1]][i - 1];
        Redge(u) {
            fa[to = ed[k].to][0] = u;
            dep[to] = dep[u] + 1; dfs(to);
        }
    }
    int lca(int u,int v){
        if (dep[u] < dep[v]) swap(u,v);
        for (int D = dep[u] - dep[v],i = 0; (1 << i) <= D; i++)
            if (D & (1 << i)) u = fa[u][i];
        if (u == v) return u;
        for (int i = 15; ~i; i--)
            if (fa[u][i] != fa[v][i]){
                u = fa[u][i];
                v = fa[v][i];
            }
        return fa[u][0];
    }
}T;
int h[maxn],ne = 1;
int n,m,N,w[maxn],pos[maxn];
int c[maxn],ci,height[maxn];
int pu[maxn],pd[maxn],su[maxn],sd[maxn];
int d[maxn],vis[maxn];
vector<int> in[maxn],out[maxn];
priority_queue<cp,vector<cp>,greater<cp> > q;
inline void build(int u,int v,int w){ed[++ne] = (EDGE){v,h[u],w}; h[u] = ne;}
inline bool cmp(const int& a,const int& b){
    return T.dfn[pos[abs(a)]] < T.dfn[pos[abs(b)]];
}
void init(){
    T.init(); ne = 1; N = 0; cls(h,0); cls(w,0);
    REP(i,n) in[i].clear(),out[i].clear();
}
void Build(){
    for (int p = 1; p <= n; p++){
        if (!in[p].size() && !out[p].size()) continue;
        ci = 0;
        for (unsigned int i = 0; i < in[p].size(); i++)
            c[++ci] = in[p][i];
        for (unsigned int i = 0; i < out[p].size(); i++)
            c[++ci] = -out[p][i];
        sort(c + 1,c + 1 + ci,cmp);
        for (int i = 1; i <= ci; i++){
            pu[i] = ++N,pd[i] = ++N;
            su[i] = ++N,sd[i] = ++N;
            if (i > 1){
                build(pu[i - 1],pu[i],0);
                build(pd[i - 1],pd[i],0);
                build(su[i],su[i - 1],0);
                build(sd[i],sd[i - 1],0);
            }
            if (c[i] >= 0) build(c[i],pu[i],0),build(c[i],su[i],0);
            else c[i] *= -1,build(pd[i],c[i],0),build(sd[i],c[i],0);
        }
        for (int i = 1; i < ci; i++){
            int tmp = T.dep[T.lca(pos[c[i]],pos[c[i + 1]])];
            build(pu[i],pd[i + 1],tmp);
            build(su[i + 1],sd[i],tmp);
        }
    }
}
void dijkstra(){
    for (int i = 0; i <= N; i++) d[i] = INF,vis[i] = false;
    for (unsigned int j = 0; j < out[1].size(); j++)
        q.push(mp(d[out[1][j]] = 0,out[1][j]));
    int u;
    while (!q.empty()){
        u = q.top().second; q.pop();
        if (vis[u]) continue;
        vis[u] = true;
        Redge(u) if (!vis[to = ed[k].to] && d[to] > d[u] + ed[k].w + w[u]){
            d[to] = d[u] + ed[k].w + w[u];
            q.push(mp(d[to],to));
        }
    }
}
void print(){
    for (int i = 2; i <= n; i++){
        int ans = INF;
        for (unsigned int j = 0; j < in[i].size(); j++)
            ans = min(ans,d[in[i][j]] + w[in[i][j]]);
        printf("%d\n",ans);
    }
}
int main(){
    int Case = read();
    while (Case--){
        n = read(); m = read(); T.n = read(); init();
        int a,b;
        REP(i,m){
            a = read(); b = read();
            w[i] = read(); pos[i] = read();
            out[a].push_back(i);
            in[b].push_back(i);
        }
        N = m;
        for (int i = 1; i < T.n; i++){
            a = read(); b = read(); read();
            T.build(a,b);
        }
        T.dfs(1);
        Build();
        dijkstra();
        print();
    }
    return 0;
}

转载于:https://www.cnblogs.com/Mychael/p/9296099.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值