BZOJ2870 最长道路tree

BZOJ2870 最长道路tree

题面:又是权限题。。。

解析

这种含多个参数(其中有有关最值的参数)的最优值求解都是套路啊。大概就是通过排序来消除一个参数的影响,然后维护答案。那么这道题就是按权值从大到小排序依次加入,然后当前权值乘上当前最长链即可。如何维护当前最长链呢?发现当两棵树合并后,新树直径的两个端点一定是原来两个直径的四个端点中的两个,然后就可以用并查集维护了。证明用反证法即可。

代码


#include<cmath>
#include<cstdio>
#include<iostream>
#include<algorithm>
#define LL long long
#define N 50005
using namespace std;
const int nlog=16;
inline int In(){
    char c=getchar(); int x=0,ft=1;
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') ft=-1;
    for(;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
    return x*ft;
}
int n,val[N],id[N],t[10];
bool vis[N];
inline bool cmp(int a,int b){
    return val[a]>val[b];
}
int h[N],e_tot=0;
struct E{ int to,nex; }e[N<<1];
inline void add(int u,int v){
    e[++e_tot]=(E){v,h[u]}; h[u]=e_tot;
    e[++e_tot]=(E){u,h[v]}; h[v]=e_tot;
}
int d[N],p[N*2][nlog+5],c[N],dfs_clock=0;
void dfs(int u,int pre,int dep){
    d[u]=dep; p[++dfs_clock][0]=u; c[u]=dfs_clock;
    for(int i=h[u],v;i;i=e[i].nex){
        v=e[i].to; if(v==pre) continue;
        dfs(v,u,dep+1); p[++dfs_clock][0]=u;
    }
}
int po[nlog+5];
inline void get_st(){
    po[0]=1; for(int i=1;i<=nlog;++i) po[i]=po[i-1]*2;
    for(int j=1;j<=nlog;++j)
    for(int i=1;i+po[j]<=dfs_clock;++i)
    p[i][j]=d[p[i][j-1]]<d[p[i+po[j-1]][j-1]]
    ?p[i][j-1]:p[i+po[j-1]][j-1];
}
inline int LCA(int x,int y){
    if(c[x]>c[y]) swap(x,y);
    int k=log(c[y]-c[x]+1)/log(2);
    return d[p[c[x]][k]]<d[p[c[y]-po[k]+1][k]]
    ?p[c[x]][k]:p[c[y]-po[k]+1][k];
}
inline int dist(int x,int y){
    return d[x]+d[y]-2*d[LCA(x,y)]+1;
}
int mx_d; LL ans=0;
int f[N],usz[N],_p[N][2],_w[N];
inline int find(int x){
    return f[x]=(f[x]==x?x:find(f[x]));
}
inline void unit(int x,int y){
    int a=find(x),b=find(y);
    if(usz[a]<usz[b]) swap(a,b);
    usz[a]+=usz[b]; f[b]=a;
    t[1]=_p[a][0]; t[2]=_p[a][1];
    t[3]=_p[b][0]; t[4]=_p[b][1];
    for(int i=1;i<4;++i)
    for(int j=i+1;j<=4;++j){
        int dis=dist(t[i],t[j]);
        if(dis>_w[a]){
            _w[a]=dis;
            _p[a][0]=t[i];
            _p[a][1]=t[j];
        }
    }
    mx_d=max(mx_d,_w[a]);
}
int main(){
    n=In(); mx_d=1;
    for(int i=1;i<=n;++i){
        val[i]=In(); id[i]=i;
        f[i]=i; usz[i]=1;
        _p[i][0]=_p[i][1]=i; _w[i]=1;
        ans=max(ans,(LL)val[i]);
    }
    sort(id+1,id+1+n,cmp);
    for(int i=1;i<n;++i) add(In(),In());
    dfs(1,0,1); get_st();
    for(int j=1,u;j<=n;++j){
        u=id[j]; vis[u]=1;
        for(int i=h[u],v;i;i=e[i].nex){
            v=e[i].to; if(vis[v]==1) unit(u,v);
        }
        ans=max(ans,(LL)val[u]*mx_d);
    }
    printf("%lld\n",ans);
    return 0;
}


转载于:https://www.cnblogs.com/pkh68/p/10561731.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值