bzoj2870: 最长道路tree(边分治)

该博客介绍了如何使用边分治的方法解决最长道路问题,通过三度化简化复杂度,提供了解决此问题的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门
边分治板题。。。
三度化即可。
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
    static char buf[rlen],*ib,*ob;
    (ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
    return ib==ob?-1:*ib++;
}
inline int read(){
    int ans=0;
    char ch=gc();
    while(!isdigit(ch))ch=gc();
    while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
    return ans;
}
typedef pair<int,int> pii;
typedef long long ll;
const int N=200005,inf=0x3f3f3f3f;
int n,vl[N],m,all,rt,siz[N];
bool vis[N];
pii Rt;
int Len;
vector<int>e[N],Son[N];
vector<pii>g[N],pth[2];
ll ans=0;
void dfs(int p,int fa){
    for(ri i=0,v;i<e[p].size();++i){
        if((v=e[p][i])==fa)continue;
        Son[p].push_back(v),dfs(v,p);
    }
}
inline void rebuild(){
    m=n;
    for(ri v1,v2,up,i=1;i<=m;++i){
        up=Son[i].size();
        if(up<3){
            for(ri j=0;j<up;++j){
                g[i].push_back(pii(Son[i][j],Son[i][j]<=n));
                g[Son[i][j]].push_back(pii(i,Son[i][j]<=n));
            }
            continue;
        }
        else{
            vl[v1=++m]=vl[i];
            g[i].push_back(pii(v1,0)),g[v1].push_back(pii(i,0));
            vl[v2=++m]=vl[i];
            g[i].push_back(pii(v2,0)),g[v2].push_back(pii(i,0));
            for(ri j=0;j<up;++j)Son[j&1?v1:v2].push_back(Son[i][j]);
        }
    }
}
void dfs1(int p,int fa,int sz){
    siz[p]=1;
    for(ri t,i=0,v;i<g[p].size();++i){
        if((v=g[p][i].fi)==fa||vis[v])continue;
        dfs1(v,p,sz),siz[p]+=siz[v];
        if((t=max(siz[v],sz-siz[v]))<all){
            all=t;
            Rt=pii(p,v);
            Len=g[p][i].se;
        }
    }
}
void dfs2(int p,int fa,int len,int mn,bool op){
    mn=min(vl[p],mn);
    pth[op].push_back(pii(len,mn));
    for(ri i=0,v;i<g[p].size();++i){
        if((v=g[p][i].fi)==fa||vis[v])continue;
        dfs2(v,p,len+g[p][i].se,mn,op);
    }
}
inline bool cmp(const pii&a,const pii&b){return a.se>b.se;}
void solve(int p,int sz){
    all=inf;
    dfs1(p,0,sz);
    if(all==inf)return;
    int a=Rt.fi,b=Rt.se,n1,n2;
    pth[0].clear(),vis[b]=1,dfs2(a,0,0,inf,0),vis[b]=0,n1=pth[0].size()-1;
    sort(pth[0].begin(),pth[0].end(),cmp);
    pth[1].clear(),vis[a]=1,dfs2(b,0,0,inf,1),vis[a]=0,n2=pth[1].size()-1;
    sort(pth[1].begin(),pth[1].end(),cmp);
    for(ri mxl=0,i=0,j=0;i<=n1;++i){
        while(j<=n2&&pth[1][j].se>=pth[0][i].se)mxl=max(mxl,pth[1][j++].fi);
        if(j)ans=max(ans,(ll)(pth[0][i].fi+mxl+Len+1)*pth[0][i].se);
    }
    for(ri mxl=0,i=0,j=0;i<=n2;++i){
        while(j<=n1&&pth[0][j].se>=pth[1][i].se)mxl=max(mxl,pth[0][j++].fi);
        if(j)ans=max(ans,(ll)(pth[1][i].fi+mxl+Len+1)*pth[1][i].se);
    }
    vis[b]=1,solve(a,sz-siz[b]),vis[b]=0;
    vis[a]=1,solve(b,siz[b]),vis[a]=1;
}
int main(){
    #ifdef ldxcaicai
    freopen("lx.in","r",stdin);
    #endif
    n=read();
    for(ri i=1;i<=n;++i)vl[i]=read();
    for(ri i=1,u,v;i<n;++i){
        u=read(),v=read();
        e[u].push_back(v),e[v].push_back(u);
    }
    dfs(1,0);
    rebuild();
    solve(1,m);
    cout<<ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值