洛谷 P4886 快递员

怎甘沉沦 提交于 2019-11-30 21:45:26

传送门

介是你没有体验过的\(chuan\)新做法。

暴力点分治算出每个点作为中心时的答案。

对于路径\(c\rightarrow u\rightarrow c\rightarrow v\rightarrow c\)的长度除以\(2\),我们可以看做\(dis(u,v)+c\)到路径\((u,v)\)的距离乘\(2\)

把路径分成两类:

经过分治中心的:

对于这种路径,\(dfs\)一遍树,当脱离了某条路径时,比如走边\((2,4)\)\((2,5)\),开始计算该路径的贡献。假设走边\((x,y)\)脱离了该路径,则\(y\)子树内每个点\(i\)都会有\(dis(i,x)\times 2+dis(u,v)\)的贡献。

具体实现:给每条边维护一个权值\(V\),对每条路径上的边的\(V\)\(dis(u,v)\)\(\max\)。算出每个点每条出边\(V\)的最大值和次大值。\(dfs\)时维护当前最大贡献\(now\),对每个点,等于该点最大值的边选次小值更新\(now\),否则选最大值,累加边权乘\(2\)即可。

这样会发现从根节点开始\(dfs\)时会出现这种情况:

此时,红色路径和绿色路径为最大和次大。

由于一条路径可能会占据根节点的两条出边,维护最大值和次大值就出\(bug\)了。

这样我们直接维护一个堆把每条路径加进去,从根节点出发时把每条经过该点的路径删掉,统计完再加回去。


不经过分治中心的:

\((u,v)\)\(lca\)标记为关键点,并赋予一个权值\(mx\)所有标记它的点对中最大的\(dis(u,v)\)。记\(ma(i)\)\(i\)到其子树内所有关键点中(设关键点为\(p\)),最大的\(mx(p)+dis(p,i)\times 2\)

转移很简单:\(ma(i)=\max\{mx(i),\max\limits_{edge(i,j)}\{ma(j)+edge(i,j).l\times 2\}\}\)

对根维护一个\(ma1\)\(ma2\)表示根的儿子中最大和次大的\(ma\)

\(dfs\)根的每个儿子\(i\)的子树,同样的,若\(ma(i)=ma1\)\(ma2\),否则选\(ma1\)\(now\),对每个点\(j\)的贡献就是\(now+dis(root,j)\times 2\)

注意不用也不能在统计时进一步更新\(now\)了。


繁琐的细节:

  • 分类路径时,可以巧妙运用\(tarjan\ lca\)
  • 处理边的\(V\)时,其实就是链取\(\max\)单点查。由于这些路径都经过根节点,直接对两端点取\(\max\),统计子树最大值即可。
  • 直接位于路径\((u,v)\)的点算不上\((u,v)\)的贡献,需要额外处理,可以在统计\(V\)时一块算,免去了树剖两个\(\log\)\(lct\)的大常数。
  • 由于一些神奇的原因,形如\((u,u)\)的路径不会被算到,把\(u\)拆出来\(u'\)连上长为\(0\)的边,把路径改为\((u,u')\)即可。

关于正确性:

如果一条路径经过根节点,就会统计上它对当前树内所有点的贡献。

否则会统计它对和它不属于根节点同一个儿子的子树的节点的贡献,然后分治它的子树。

我们就能把它对每个点的贡献算上。

关于复杂度:

随便想一想就会发现复杂度是\(O((m+n)\log n)\)的。

代码:

我的\(priority\_queue\)不知道为啥会在程序结束时炸掉于是手写的堆。

尽量精简了代码还是有\(200+\)行毕竟是暴力。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
#include <queue>

#define maxn 200005
#define inf 0x3f3f3f3f

using namespace std;

inline int read(){
    int x=0,y=0;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return y?-x:x;
}
namespace origin{
    int h[maxn],num;
    struct edge{
        int pre,to,l;
    }e[maxn<<1];
    inline void add(int from,int to,int l){
        e[++num].pre=h[from],h[from]=num,e[num].to=to,e[num].l=l;
        e[++num].pre=h[to],h[to]=num,e[num].to=from,e[num].l=l;
    }
}
int h[maxn],siz[maxn],top[maxn],son[maxn],deep[maxn],fa[maxn],F[maxn],qu[maxn][2],num,all,mx,root,cnt;
int ma1[maxn],ma2[maxn],ema1[maxn],ema2[maxn],ans[maxn],dis[maxn],ee[maxn],bl[maxn],sp[maxn];
vector<pair<int,int> >v[maxn];
vector<int>poi[maxn];
bool vis[maxn],ap[maxn];
struct heap{
    int H[maxn],heap_size;
    void push(int d){
        int now=++heap_size;
        H[heap_size]=d;
        while(now>1){
            if(H[now]<H[now>>1]) return;
            swap(H[now],H[now>>1]);
            now>>=1;
        }
    }
    void pop(){
        int now=1,next;
        H[1]=H[heap_size--];
        while((now<<1)<=heap_size){
            next=now<<1;
            if(next<heap_size&&H[next+1]>H[next]) next++;
            if(H[now]>H[next]) return;
            swap(H[now],H[next]);
            now=next;                      
        } 
    }
    bool empty(){return !(bool(heap_size));}
};
struct del_heap{
    heap q,d;
    void push(int x){q.push(x);}
    void del(int x){d.push(x);}
    void clear(){q.heap_size=d.heap_size=0;}
    int top(){
        while(!d.empty()&&q.H[1]==d.H[1])q.pop(),d.pop();
        if(q.empty())return -inf;
        return q.H[1];
    }
}q;
struct edge{
    int pre,to,l,v;
}e[maxn<<1];
int find(int x){
    if(F[x]==x)return x;
    return F[x]=find(F[x]);
}
inline void add(int from,int to,int l){
    e[++num].pre=h[from],h[from]=num,e[num].to=to,e[num].l=l,e[num].v=-inf;
}
void dfs1(int node=1){
    siz[node]=1;
    int x;
    for(register int i=h[node];i;i=e[i].pre){
        x=e[i].to;
        if(siz[x])continue;
        fa[x]=node,deep[x]=deep[node]+1,dis[x]=dis[node]+e[i].l;
        dfs1(x),siz[node]+=siz[x];
        if(siz[x]>siz[son[node]])son[node]=x;
    }
}
void dfs2(int node=1){
    vis[node]=1;
    if(son[node]){
        top[son[node]]=top[node],dfs2(son[node]);
        int x;
        for(register int i=h[node];i;i=e[i].pre){
            x=e[i].to;
            if(!vis[x])top[x]=x,dfs2(x);
        }
    }
}
inline int lca(int x,int y){
    while(top[x]!=top[y])deep[top[x]]<deep[top[y]]?y=fa[top[y]]:x=fa[top[x]];
    return deep[x]<deep[y]?x:y;
}
inline void check(int &m1,int &m2,int x){
    if(x>m1)m2=m1,m1=x;
    else if(x>m2)m2=x;
}
void getroot(int node,int f){
    int x,ma=0;
    for(register int i=h[node];i;i=e[i].pre){
        x=e[i].to;
        if(x==f||vis[x])continue;
        getroot(x,node),ma=max(ma,siz[x]);
    }
    ma=max(ma,all-siz[node]);
    if(ma<mx)mx=ma,root=node;
}
void init(int node,int f,int b){
    int x;
    bl[node]=b,ema1[node]=ema2[node]=ma1[node]=ma2[node]=-inf,ap[node]=1,F[node]=node;
    for(vector<pair<int,int> >::iterator iter=v[node].begin();iter!=v[node].end();++iter)
        if(ap[iter->first]){
            x=find(iter->first);
            if(x!=root)ma1[x]=max(ma1[x],iter->second);
            else {
                ee[iter->first]=max(ee[iter->first],iter->second),ee[node]=max(ee[node],iter->second);
                ma2[node]=max(ma2[node],iter->second),poi[b].push_back(iter->second);
                if(iter->first!=root)poi[bl[iter->first]].push_back(iter->second),ma2[iter->first]=max(ma2[iter->first],iter->second);
                q.push(iter->second);
            }
        }
    for(register int i=h[node];i;i=e[i].pre){
        x=e[i].to;
        if(x==f||vis[x])continue;
        init(x,node,b),F[x]=node,ma1[node]=max(ma1[node],ma1[x]+(e[i].l<<1));
    }
}
void calc(int node,int f,int now){
    siz[node]=1,ans[node]=max(ans[node],now);
    int x;
    for(register int i=h[node];i;i=e[i].pre){
        x=e[i].to;
        if(x==f||vis[x])continue;
        calc(x,node,max(e[i].v==ema1[node]?ema2[node]:ema1[node],max(now,ma2[node]))+(e[i].l<<1));
        siz[node]+=siz[x];
    }
}
void calc_edge_value(int node,int f){
    int x;
    for(register int i=h[node];i;i=e[i].pre){
        x=e[i].to;
        if(x==f||vis[x])continue;
        calc_edge_value(x,node);
        ee[node]=max(ee[node],ee[x]),check(ema1[node],ema2[node],e[i].v=ee[x]);
    }
    ans[node]=max(ans[node],ee[node]);
}
void clear(int node,int f){
    ap[node]=0,ee[node]=-inf;
    int x;
    for(register int i=h[node];i;i=e[i].pre){
        x=e[i].to;
        if(x==f||vis[x])continue;
        clear(x,node),e[i].v=-inf;
    }
}
void solve(int node){
    q.clear(),vis[node]=1;
    int x;
    ma1[node]=ma2[node]=-inf,F[node]=node,ap[node]=1,bl[node]=0;
    for(register int i=h[node];i;i=e[i].pre){
        x=e[i].to;
        if(vis[x])continue;
        init(x,node,x),F[x]=node,check(ma1[node],ma2[node],ma1[x]+(e[i].l<<1));
    }
    calc_edge_value(node,0);
    ans[node]=max(ans[node],ma1[node]);
    for(register int i=h[node];i;i=e[i].pre){
        x=e[i].to;
        if(vis[x])continue;
        for(register int j=0;j<poi[x].size();++j)q.del(poi[x][j]);
        calc(x,node,max((ma1[x]+(e[i].l<<1)==ma1[node]?ma2[node]:ma1[node]),q.top())+(e[i].l<<1));
        for(register int j=0;j<poi[x].size();++j)q.push(poi[x][j]);
        poi[x].clear();
    }
    clear(node,0);
    for(register int i=h[node];i;i=e[i].pre){
        x=e[i].to;
        if(vis[x])continue;
        mx=inf,all=siz[x],getroot(x,node),solve(root);
    }
}
void rebuild(int node=1,int f=0,int gf=0){
    int x;  
    if(node>all)for(register int i=h[node];i;i=e[i].pre){
        x=e[i].to;
        if(x==f)continue;
        rebuild(x,node,f);
    }
    else for(register int i=origin::h[node];i;i=origin::e[i].pre){
        x=origin::e[i].to;
        if(x==f||x==gf)continue;
        if(sp[x])add(node,sp[x],origin::e[i].l),add(sp[x],node,origin::e[i].l),add(sp[x],x,0),add(x,sp[x],0),x=sp[x];
        else add(node,x,origin::e[i].l),add(x,node,origin::e[i].l);
        rebuild(x,node,f);
    }
}
int DIS(int x,int y){
    return dis[x]+dis[y]-(dis[lca(x,y)]<<1);
}
int main(){
    memset(ee,~0x3f,sizeof ee);
    int n=all=read(),m=read(),k=n,x,y,z,res=inf;
    for(register int i=1;i<n;++i)x=read(),y=read(),z=read(),origin::add(x,y,z);
    for(register int i=1;i<=m;++i){
        qu[i][0]=x=read(),qu[i][1]=y=read();
        if(x==y&&!sp[x])sp[x]=++k,qu[i][1]=k;
    }
    if(sp[1])add(sp[1],1,0),add(1,sp[1],0);
    rebuild(1,sp[1]),dfs1(),dfs2(); 
    for(register int i=1;i<=m;++i){
        x=qu[i][0],y=qu[i][1],z=dis[x]+dis[y]-(dis[lca(x,y)]<<1);
        v[x].push_back((pair<int,int>){y,z}),v[y].push_back((pair<int,int>){x,z});
    }
    memset(vis,0,sizeof vis);
    all=k,mx=inf,getroot(1,0),solve(root);
    for(register int i=1;i<=n;++i)res=min(res,ans[i]);
    printf("%d\n",res);
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!