最大生成树+树链剖分——Luogu1967 [NOIP2013]货车运输

题面:Luogu1967
乍一看,网络流水题?
不存在的,有多组询问+起始点和终止点各不相同。。。
那不能用网络流还能用什么?
我们可以发现,题目所要求的那条路径一定在当前连通块的最大生成树上
这是一定的,否则找不到更大的一条路径里边的最小值比这条更大
所以就很容易想到Kruskal求出最大生成森林(图不一定连通)之后在树上进行操作了
具体什么操作呢?就是找两点间在树上路径的最小值即可
如果两点属于不同生成树,输出-1
看其他网上题解都是啥倍增一下或者找LCA之类的东西
我呢直接大力树剖,当然啦调了半个小时QAQ

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<queue>
#include<string>
#include<ctime>
#include<map>
#include<climits>
#include<set>
using namespace std;
struct ppap{int x,y,v;}a[100001];
inline bool cmp(ppap a,ppap b){return a.v>b.v;}
int nedge=0,p[200001],c[200001],nex[200001],head[200001];
int n,m,gfa[100001],fa[100001],deep[100001],s[100001],son[100001],top[100001],jzq[400001];
int sum=0,xs[400001],sx[400001],lt[400001],rt[400001],t[400001];
inline void addedge(int x,int y,int z){
    p[++nedge]=y;c[nedge]=z;nex[nedge]=head[x];head[x]=nedge;
}
inline int getfather(int v){return gfa[v]==v?v:gfa[v]=getfather(gfa[v]);}
inline void dfs(int x,int Fa,int dep){
    fa[x]=Fa;deep[x]=dep;s[x]=1;
    for(int k=head[x];k;k=nex[k])if(p[k]!=Fa){
        dfs(p[k],x,dep+1);s[x]+=s[p[k]];
        if(!son[x]||s[p[k]]>s[son[x]])son[x]=p[k];
    }
}
inline void dfss(int x,int g,int qz){
    top[x]=g;sx[x]=++sum;xs[sum]=x;jzq[x]=qz;
    if(!son[x])return;
    for(int k=head[x];k;k=nex[k])if(p[k]==son[x]){dfss(son[x],g,c[k]);break;}
    for(int k=head[x];k;k=nex[k])if(p[k]!=fa[x]&&p[k]!=son[x])dfss(p[k],p[k],c[k]);
}
inline void build(int l,int r,int nod){
    lt[nod]=l;rt[nod]=r;
    if(l==r){t[nod]=jzq[xs[l]];return;}
    int mid=l+r>>1;
    build(l,mid,nod*2);build(mid+1,r,nod*2+1);
    t[nod]=min(t[nod*2],t[nod*2+1]);
}
inline int ssum(int i,int j,int nod){
    if(lt[nod]>=i&&rt[nod]<=j)return t[nod];
    int mid=lt[nod]+rt[nod]>>1,ans=1e9;
    if(i<=mid)ans=min(ans,ssum(i,j,nod*2));
    if(j>mid)ans=min(ans,ssum(i,j,nod*2+1));
    return ans;
}
inline int fsum(int x,int y){
    int fx=top[x],fy=top[y],ans=1e9;
    while(fx!=fy){
        if(deep[fx]<deep[fy])swap(fx,fy),swap(x,y);
        ans=min(ans,ssum(sx[fx],sx[x],1));
        x=fa[fx];fx=top[x];
    }
    if(deep[x]>deep[y])swap(x,y);
    ans=min(ans,ssum(sx[x]+1,sx[y],1));
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].v);
    sort(a+1,a+m+1,cmp);
    for(int i=1;i<=n;i++)gfa[i]=i;
    int cnt=0;
    for(int i=1;i<=m;i++){
        int fx=getfather(a[i].x),fy=getfather(a[i].y);
        if(fx==fy)continue;cnt++;
        gfa[fx]=fy;addedge(a[i].x,a[i].y,a[i].v);addedge(a[i].y,a[i].x,a[i].v);
        if(cnt==n-1)break;
    }//以上Kruskal求最大生成树
    for(int i=1;i<=n;i++)if(!deep[i])dfs(i,0,1),dfss(i,0,0);//从这里开始树剖
    build(1,n,1);
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        int x,y;scanf("%d%d",&x,&y);
        int fx=getfather(x),fy=getfather(y);
        if(fx!=fy)puts("-1");//判连通
        else printf("%d\n",fsum(x,y));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值