最長道路tree【邊分治】

題目鏈接 BZOJ 2870


給定一棵N個點的樹,求樹上一條鏈使得鏈的長度乘鏈上所有點中的最小權值所得的積最大。

其中鏈長度定義爲鏈上點的個數。


  邊分治與點分治類似,都是樹分治的一種,與點分治不同的是,邊分治找的是分治中心邊,而點分治找的是分治重心點。

  分治操作是解決了複雜度現在,我們要以我們的分治中心邊爲基礎向兩邊延伸,所以,至少向兩邊各自延伸一個點。

  但是這樣做的複雜度顯然是會變成O(N^2)級別的了,我們在這裏還需要考慮到優化。

  將點存進數組中,那麼往兩個方向,就會有各自的數組了,然後呢,查詢到每個點的時候,有長度以及最小權值,那麼我們就需要記錄下來長度、權值,我們按照權值降序排列,然後,我們用O(N)的去枚舉一個方向,以這個方向上的權值作爲基準,記錄另一個方向上最大長度,這裏可以用一個單調隊列維護,只要權值還大於等於,那麼它的長度就可以用來更新這個最大長度。

  然後,再反過來對另一個方向也這樣操作一下,複雜度仍然是O(N)

  所以加上樹的邊分治的複雜度O(N\log(N)),總的複雜度就是O(N\log(N))

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
//#include <unordered_map>
//#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define eps 1e-4
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 2e5 + 7;
int N, n, a[maxN], head[maxN], cnt;
ll ans = 0;
struct Eddge
{
    int nex, to, val;
    Eddge(int a=-1, int b=0, int c=0):nex(a), to(b), val(c) {}
} edge[maxN << 2];
inline void addEddge(int u, int v, int w)
{
    edge[cnt] = Eddge(head[u], v, w);
    head[u] = cnt++;
}
inline void _add(int u, int v, int w) { addEddge(u, v, w); addEddge(v, u, w); }
vector<int> E[maxN];
void dfs(int u, int fa)
{
    for(int i=head[u], v; ~i; i=edge[i].nex)
    {
        v = edge[i].to;
        if(v == fa) continue;
        E[u].push_back(v);
        dfs(v, u);
    }
}
void ReBuild()
{
    cnt = 0; for(int i=1; i<=(N<<1); i++) head[i] = -1;
    for(int u=1, len, lc, rc; u<=N; u++)
    {
        len = (int)E[u].size();
        if(len <= 2)
        {
            for(int i=0; i<len; i++) _add(u, E[u][i], E[u][i] <= n);
        }
        else
        {
            lc = ++N; rc = ++N; a[lc] = a[rc] = a[u];
            _add(u, lc, 0); _add(u, rc, 0);
            for(int i=0; i<len; i++)
            {
                if(i & 1) E[lc].push_back(E[u][i]);
                else E[rc].push_back(E[u][i]);
            }
        }
    }
}
int siz[maxN], maxx, root;
bool vis[maxN] = {false};
void findroot(int u, int fa, int sum)
{
    siz[u] = 1;
    for(int i=head[u], v, mx_siz; ~i; i=edge[i].nex)
    {
        v = edge[i].to;
        if(vis[i >> 1] || v == fa) continue;
        findroot(v, u, sum);
        siz[u] += siz[v];
        mx_siz = max(siz[v], sum - siz[v]);
        if(maxx > mx_siz) { maxx = mx_siz; root = i; }
    }
}
struct node
{
    int len, val;
    node(int a=0, int b=0):len(a), val(b) {}
    friend bool operator < (node e1, node e2) { return e1.val > e2.val; }
} S[2][maxN];
int top[2];
void solve_dfs(int u, int fa, int len, int mn, int op)
{
    mn = min(mn, a[u]); S[op][++top[op]] = node(len, mn);
    for(int i=head[u], v; ~i; i=edge[i].nex)
    {
        v = edge[i].to;
        if(vis[i >> 1] || v == fa) continue;
        solve_dfs(v, u, len + edge[i].val, mn, op);
    }
}
void Divide(int u, int sum)
{
    maxx = INF; findroot(u, 0, sum);
    if(maxx == INF) return;
    vis[root >> 1] = true;
    int now = root;
    top[0] = top[1] = 0;
    solve_dfs(edge[root].to, 0, 0, INF, 0);
    solve_dfs(edge[root ^ 1].to, 0, 0, INF, 1);
    sort(S[0] + 1, S[0] + top[0] + 1);
    sort(S[1] + 1, S[1] + top[1] + 1);
    int lence;
    for(int i=1, j=1, mx=0; i<=top[0]; i++)
    {
        while(j <= top[1] && S[1][j].val >= S[0][i].val) mx = max(mx, S[1][j++].len);
        lence = mx + edge[root].val + S[0][i].len;
        if(j > 1) ans = max(ans, 1LL * (lence + 1) * S[0][i].val);  //至少選一個點(中心線的另一頭)
    }
    for(int i=1, j=1, mx=0; i<=top[1]; i++)
    {
        while(j <= top[0] && S[0][j].val >= S[1][i].val) mx = max(mx, S[0][j++].len);
        lence = mx + edge[root].val + S[1][i].len;
        if(j > 1) ans = max(ans, 1LL * (lence + 1) * S[1][i].val);
    }
    Divide(edge[now].to, siz[edge[now].to]);
    Divide(edge[now ^ 1].to, sum - siz[edge[now].to]);
}
inline void init()
{
    cnt = 0;
    for(int i=1; i<=N; i++) head[i] = -1;
}
int main()
{
    scanf("%d", &N); n = N;
    init();
    for(int i=1; i<=N; i++) scanf("%d", &a[i]);
    for(int i=1, u, v; i<N; i++)
    {
        scanf("%d%d", &u, &v);
        _add(u, v, 1);
    }
    dfs(1, 0);
    ReBuild();
    Divide(1, N);
    printf("%lld\n", ans);
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章