卡车——题解【2015计蒜之道复赛 京东的物流路径】

版权声明:发现蒟蒻ff_666,但转载请注明 https://blog.csdn.net/qq_42403731/article/details/82182244

题目大意

给出一棵树,有点权val[]、边权w[]
m a x ( m i n ( v a l i ) w i ) (其中 v a l i w i 都是处于x,y两点间)
N <= 200000 , 1 <= v a l i , w i <= 1000000000

直接给出官方题解

将所有点按照权值从大到小排序,对于将当前点和与其相连的所有点依次合并到一个集合中。并查集需要维护当前集合中的最长路径长度和对应的两个端点。在合并两个集合后,最终集合的最长路一定只有两类情况:一类是其中一个集合的最长路,一共有2 种;一类是由两个集合的最长路的端点互相连接而成,一共有 2×2=4 种。需要用到最近公共祖先的算法预处理求两点在树上的距离,离线处理即可。每次合并并查集之后用当前点的权值乘以最长路的总长度来更新最优结果即可。即使这个点不在当前合并后的集合的最长路上也是没有问题的,因为如果这样的话,必然已经在之前得到了对应的结果,这次合并不会对最终结果产生影响

然后感觉好绕。。
我的做法比较暴力:直接点分治
——统计从当前节点到当前根节点的点权最小值与边权之和
接下来,为了避免算错,我们只计算以当前根节点为中间点的链,即两点在当前根节点不同子树内的情况:
(区分子树可以像这样打个标记)

void DFS(int x,int fa,int k){//k为标记
    c[++top]=(ff){k,g[x]=Min(val[x],g[fa]),dst[x]};
    for(int j=lnk[x];j;j=nxt[j])if(!vis[son[j]]&&son[j]!=fa){
        dst[son[j]]=dst[x]+w[j];
        DFS(son[j],x,k);
    }
}
  • 首先我们对点按最小点权从大到小排序,那么当前处理到 i 的最小点权显然就是 i 的最小点权
  • 既然点权已经固定,那么我们要找的显然就是1~~ i -1中到当前根的最大距离,这个显然每次 O ( 1 ) 更新一下就好了
  • 但这样还有个问题——万一最大距离与当前点在同一子树怎么办?那就再记个次大值呗!(当然要不在当前最大值所在的子树)
    然后套点分治的模板,就OK了。。
#include<cstdio>
#include<algorithm>
#define gt() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++)
#define LL long long
using namespace std;
static char buf[1000000],*p1=buf,*p2=buf;
const int maxn=(2e5)+5;
template<typename Tp>inline Tp Max(Tp x,Tp y){return x>y?x:y;}
template<typename Tp>inline Tp Min(Tp x,Tp y){return x<y?x:y;}
int n,rot,sum,top,ss,g[maxn],f[maxn],cnt[maxn],val[maxn];LL Ans,dst[maxn];bool vis[maxn];
int tot,lnk[maxn],nxt[maxn<<1],son[maxn<<1],w[maxn<<1];
void add_e(int x,int y,int z){son[++tot]=y,w[tot]=z,nxt[tot]=lnk[x],lnk[x]=tot;}
struct ff{
    int x,min_x;LL dis;
    bool operator <(const ff b)const{return b.min_x<min_x;}
}c[maxn];
int read(){
    int ret=0;char ch=gt();
    while(ch<'0'||ch>'9') ch=gt();
    while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=gt();
    return ret;
}
void getroot(int x,int fa){
    f[x]=0,cnt[x]=1;
    for(int j=lnk[x];j;j=nxt[j])if(!vis[son[j]]&&son[j]!=fa){
        getroot(son[j],x);
        cnt[x]+=cnt[son[j]];
        if(cnt[son[j]]>f[x]) f[x]=cnt[son[j]];
    }
    if(sum-cnt[x]>f[x]) f[x]=n-cnt[x];
    if(f[x]<f[rot]) rot=x;
}
void DFS(int x,int fa,int k){
    c[++top]=(ff){k,g[x]=Min(val[x],g[fa]),dst[x]};
    for(int j=lnk[x];j;j=nxt[j])if(!vis[son[j]]&&son[j]!=fa){
        dst[son[j]]=dst[x]+w[j];
        DFS(son[j],x,k);
    }
}
void solve(int x){
    ss=0,c[top=1]=(ff){1,g[x]=val[x],dst[x]=0};
    for(int j=lnk[x];j;j=nxt[j])if(!vis[son[j]]){
        dst[son[j]]=w[j];
        DFS(son[j],x,++ss);
    }
    sort(c+1,c+1+top);
    int id1=0,id2=0;LL max_x=0,max_y=0;
    for(int i=1;i<=top;i++){
        if(c[i].x!=id1) Ans=Max(Ans,(max_x+c[i].dis)*c[i].min_x);else Ans=Max(Ans,(max_y+c[i].dis)*c[i].min_x);
        if(c[i].dis>max_x){
            if(c[i].x==id1) max_x=c[i].dis;else id2=id1,max_y=max_x,id1=c[i].x,max_x=c[i].dis;
        }else if(c[i].x!=id1&&c[i].dis>max_y) id2=c[i].x,max_y=c[i].dis;
    }
}
void work(int x){
    vis[x]=1,solve(x);
    for(int j=lnk[x];j;j=nxt[j])if(!vis[son[j]]){
        rot=0,sum=cnt[son[j]];
        getroot(son[j],x),work(rot);
    }
}
int main(){
    n=read();
    for(int i=1;i<=n;i++) val[i]=read();
    for(int j=1;j<n;j++){
        int x=read(),y=read(),z=read();
        add_e(x,y,z),add_e(y,x,z);
    }
    f[0]=maxn,sum=n,getroot(1,0);
    work(rot);
    printf("%lld\n",Ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42403731/article/details/82182244