BJ模拟:路径规划(树的直径)

传送门

题解:
边从小到大加入即可,我们只需要动态维护树的直径和经过当前边的最长链。
显然动态树可做,不过为 O(nlog2n) O ( n log 2 ⁡ n )

直径有优美的性质,如果合并两颗树 S,T S , T ,之前的直径分别为 (Sx,Sy),(Tx,Ty) ( S x , S y ) , ( T x , T y ) ,那么新的直径一定是 (Sx,Sy),(Sx,Tx),(Sx,Ty)(Sy,Tx),(Sy,Ty),(Tx,Ty) ( S x , S y ) , ( S x , T x ) , ( S x , T y ) ( S y , T x ) , ( S y , T y ) , ( T x , T y ) 中的一个。 反证法很好证明。

同时容易证明经过当前边的最长链在 (Sx,Tx),(Sx,Ty)(Sy,Tx),(Sy,Ty) ( S x , T x ) , ( S x , T y ) ( S y , T x ) , ( S y , T y ) 中产生。

树链剖分,时间复杂度 O(nlogn) O ( n log ⁡ n )

#include 
using namespace std;
typedef long long LL;
typedef pair <int,int> pii;

const int RLEN=1<<18|1;
inline char nc() {
    static char ibuf[RLEN],*ib,*ob;
    (ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
    return (ib==ob) ? -1 : *ib++;
}
inline int rd() {
    char ch=nc(); int i=0,f=1;
    while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
    while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
    return i*f;
}
inline void W(LL x) {
    static int buf[50];
    if(!x) {putchar('0'); return;}
    if(x<0) {putchar('-'); x=-x;}
    while(x) {buf[++buf[0]]=x%10; x/=10;}
    while(buf[0]) putchar(buf[buf[0]--]+'0');
}

const int N=3e5+50;

int n;
vector  edge[N];
int dep[N], fa[N], top[N], sze[N], son[N], anc[N];
LL dis[N],ans;

struct chain {
    int x,y; LL dis;
    friend inline bool operator <(const chain &a,const chain &b) {return a.disstruct E {
    int x,y,a;
    friend inline bool operator <(const E &a,const E &b) {return a.ainline void dfs(int x,int f) {
    sze[x]=1; anc[x]=x;
    for(auto v:edge[x]) {
        if(v.first==f) continue;
        fa[v.first]=x; 
        dep[v.first]=dep[x]+1;
        dis[v.first]=dis[x]+v.second;
        dfs(v.first,x);
        sze[x]+=sze[v.first];
        son[x] = (sze[son[x]] < sze[v.first]) ? v.first : son[x];
    }
}
inline void dfs2(int x,int f) {
    for(auto v:edge[x]) {
        if(v.first==f) continue;
        top[v.first]=(v.first == son[x]) ? top[x] : v.first;
        dfs2(v.first,x);
    }
}
inline int lca(int x,int y) {
    while(top[x]!=top[y]) {
        (dep[top[x]] > dep[top[y]]) ? (x=fa[top[x]]) : (y=fa[top[y]]);
    } return (dep[x] < dep[y]) ? x : y;
}
inline chain Dis(int x,int y) {return (chain){x,y,dis[x]+dis[y]-2*dis[lca(x,y)]};}
inline int ga(int x) {return (anc[x]==x) ? x : (anc[x]=ga(anc[x]));}
int main() {
    n=rd();
    for(int i=1;iint x=rd(), y=rd(), a=rd();
        edge[x].push_back(pii(y,a));
        edge[y].push_back(pii(x,a));
        e[i]=(E) {x,y,a};
    }
    dfs(1,0); top[1]=1; dfs2(1,0);
    sort(e+1,e+n);
    for(int i=1;i<=n;i++) tr[i]=(chain) {i,i,0};
    for(int i=n-1;i>=1;i--) {
        int x=e[i].x, y=e[i].y, a=e[i].a;
        if(fa[x]==y) swap(x,y);
        x=ga(x); anc[y]=x;
        chain mx=max(Dis(tr[x].x,tr[y].x), Dis(tr[x].x,tr[y].y));
        mx=max(mx, max(Dis(tr[x].y, tr[y].x), Dis(tr[x].y,tr[y].y)));
        ans=max(ans,a*mx.dis);
        tr[x]=max(tr[x], max(tr[y],mx));
    }
    cout<

你可能感兴趣的:(树链剖分)