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

来源:互联网 发布:零售软件是什么 编辑:程序博客网 时间:2024/04/30 15:06

题面: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;}