码迷,mamicode.com
首页 > 其他好文 > 详细

[UOJ 87] #87. mx的仙人掌

时间:2019-01-09 00:32:11      阅读:216      评论:0      收藏:0      [点我收藏+]

标签:memset   main   pre   namespace   sizeof   情况   lca   back   return   

#87. mx的仙人掌

UOJ 87

技术分享图片

题目大意

给出一个 \(n\) 个点,\(m\) 条边的带边权的仙人掌,定义两点之间距离为最短路长度,\(Q\) 次询问,每次给出 \(cnt\) 个点,问它们之间最远点对的距离。

数据范围

边权不超过 \(2^{31} - 1\)

\(n, \sum cnt \le 300000\)

时空限制

5s, 512MB

分析

此题如果不是仙人掌,那就是每次建出虚树来 DP

仙人掌求最短路也就是建出圆方树后根据 \(lca\) 分类讨论,那么,我们就根据圆方树建一个虚树,如果当前节点是圆点那么就和树的情况 ,如果当前节点为方点那么它的儿子提出来,在环上用单调队列更新答案即可

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <vector>
using namespace std;
inline char nc() {
    return getchar();
    static char buf[100000], *l = buf, *r = buf;
    return l==r&&(r=(l=buf)+fread(buf,1,100000,stdin),l==r)?EOF:*l++;
}
template<class T> void read(T & x) {
    x = 0; int f = 1, ch = nc();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=nc();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10-‘0‘+ch;ch=nc();}
    x *= f;
}
typedef long long ll;
const int maxn = 300000 + 5;
const int maxm = maxn * 2;
const int maxe = maxm * 2;
const int maxnode = maxn * 2;
const int maxlog = 20;
const int sigma_cnt = 300000 + 5;
int n, m, Q, cnt, p[sigma_cnt]; ll an;
int sta[sigma_cnt], que[sigma_cnt << 1];
ll f[maxnode];
int head[maxnode], ecnt;
int dfc, dfn[maxnode], low[maxn], anc[maxn]; ll dist[maxn];
int dep[maxnode], parent[maxnode][maxlog]; ll dis[maxnode];
int bcnt; ll sum[maxn];
map<int, int> E[maxn];
vector<int> adj[maxnode];
struct edge {
    int to, nex; ll cost;
    edge(int to=0, int nex=0, ll cost=0) : to(to), nex(nex), cost(cost) {}
} g[maxe];
inline void addedge1(int u, int v, int w) {
    if(E[u].count(v)) E[u][v] = min(E[u][v], w); else E[u][v] = w;
    if(E[v].count(u)) E[v][u] = min(E[v][u], w); else E[v][u] = w;
}
inline void addedge2(int u, int v, ll w) {
    g[ecnt] = edge(v, head[u], w), head[u] = ecnt++;
    g[ecnt] = edge(u, head[v], w), head[v] = ecnt++;
}
inline void addedge3(int u, int v) {
    adj[u].push_back(v);
}
void tarjan(int u) {
    low[u] = dfn[u] = ++dfc;
    for(map<int, int> :: iterator it = E[u].begin(); it != E[u].end(); ++it) {
        int v = it->first;
        if(!dfn[v]) {   
            dist[v] = dist[u] + it->second;
            anc[v] = u; tarjan(v);
            low[u] = min(low[u], low[v]);
            if(low[v] > dfn[u]) {
                addedge2(u, v, it->second);
            }
        }
        else if(anc[u] != v) {
            low[u] = min(low[u], dfn[v]);
        } 
    }
    for(map<int, int> :: iterator it = E[u].begin(); it != E[u].end(); ++it) {
        int v = it->first;
        if(anc[v] != u && dfn[v] >= dfn[u]) {
            int now = ++bcnt; sum[now] = dist[v] - dist[u] + it->second;
            for(int x = v; ; x = anc[x]) {
                ll t = dist[x] - dist[u]; addedge2(now + n, x, min(t, sum[now] - t));
                if(x == u) break;
            }
        }
    }
}
void dfs(int u) {
    dfn[u] = ++dfc;
    for(int i = 1; i < maxlog; ++i) {
        parent[u][i] = parent[ parent[u][i - 1] ][i - 1];
    }
    for(int i = head[u]; ~ i; i = g[i].nex) {
        int v = g[i].to; if(v == parent[u][0]) continue;
        dep[v] = dep[u] + 1, dis[v] = dis[u] + g[i].cost;
        parent[v][0] = u; dfs(v);
    }
}
void jump(int & u, int k) {
    for(int i = maxlog - 1; ~ i; --i) {
        if(k >= (1 << i)) {
            k -= 1 << i;
            u = parent[u][i];
        }
    }
}
int lca(int u, int v) {
    if(dep[u] > dep[v]) swap(u, v);
    jump(v, dep[v] - dep[u]);
    if(u == v) return u;
    for(int i = maxlog - 1; ~ i; --i) {
        if(parent[u][i] != parent[v][i]) {
            u = parent[u][i];
            v = parent[v][i];
        }
    }
    return parent[u][0];
}
void update(int u, int n) {
    static ll f[sigma_cnt << 1], g[sigma_cnt << 1];
    for(int i = 1; i <= n; ++i) {
        f[i] = dist[ sta[i] ], f[n + i] = f[i] + sum[u - ::n];
        g[i] = ::f[ sta[i] ], g[n + i] = g[i];
    }
    n <<= 1; int l = 0, r = -1;
    for(int i = 1; i <= n; ++i) {
        while(l <= r && (f[i] - f[ que[l] ]) * 2 > sum[u - ::n]) l++;
        if(l <= r) an = max(an, g[ que[l] ] - f[ que[l] ] + g[i] + f[i]);
        while(l <= r && g[ que[r] ] - f[ que[r] ] <=  g[i] - f[i]) r--;
        que[++r] = i;
    }
}
void dp(int u) {
    f[u] = 0; int top = 0;
    for(unsigned int i = 0; i < adj[u].size(); ++i) {
        int v = adj[u][i]; dp(v);
    }
    for(unsigned int i = 0; i < adj[u].size(); ++i) {
        int v = adj[u][i];
        if(u <= n) an = max(an, f[u] + f[v] + dis[v] - dis[u]);
        else {
            int s = v; jump(s, dep[v] - dep[u] - 1);
            f[s] = f[v] + dis[v] - dis[s]; sta[++top] = s;
        }
        f[u] = max(f[u], f[v] + dis[v] - dis[u]);

    }
    if(u > n) update(u, top);
    adj[u].clear();
}
int cmp(const int & a, const int & b) {
    return dfn[a] < dfn[b];
}
void solve() {
    read(cnt);
    for(int i = 1; i <= cnt; ++i) read(p[i]);
    sort(p + 1, p + cnt + 1, cmp), cnt = unique(p + 1, p + cnt + 1) - p - 1;
    int top = 0; sta[++top] = p[1];
    for(int i = 2; i <= cnt; ++i) {
        int w = lca(sta[top], p[i]);
        while(top > 1 && dep[ sta[top - 1] ] > dep[w]) {
            addedge3(sta[top - 1], sta[top]); top--;
        }
        if(dep[sta[top]] > dep[w]) addedge3(w, sta[top--]);
        if(w != sta[top]) sta[++top] = w;
        sta[++top] = p[i];
    }
    while(top > 1) {
        addedge3(sta[top - 1], sta[top]); top--;
    }
    an = 0; dp(sta[top]); printf("%lld\n", an);
}
int main() {
    read(n), read(m);
    for(int i = 1; i <= m; ++i) {
        int u, v, w;
        read(u), read(v), read(w);
        addedge1(u, v, w);
    }
    memset(head, -1, sizeof(head)); tarjan(1);
    for(int i = 1; i <= n; ++i) E[i].clear(); 
    dfc = 0; dfs(1);
    read(Q);
    for(int i = 1; i <= Q; ++i) solve();
    return 0;
}

[UOJ 87] #87. mx的仙人掌

标签:memset   main   pre   namespace   sizeof   情况   lca   back   return   

原文地址:https://www.cnblogs.com/ljzalc1022/p/10241991.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!