货车运输(LCA+最大生成树)

神奇传送门

恩,这是一道神奇的LCA+难度的题目。

题目是这样的: 

A 国有n座城市,编号从1到n,城市之间有 m条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。 

一开始看了看,以为是费用流(PS:一看就是费用流裸题嘛。。)

然后发现有Q个询问。。

完美TLE

然后zxyer狠狠D了我一顿(%%%)这才发现写LCA是正解。。(太像了woc)

好吧,说正解。

这道题出现了Q个询问,我们很快就会想到RMQ和LCA

可是这道题是个图,不一定是树。

根据最大生成树的性质(载重量最大的那条路径一定在最大生成树上)

RMQ处理区间最小值,而LCA有个性质,就是它一定在U到V的最短路上,而且是2个点到根节点的路径的第一个交点。所以到了LCA就没必要往上走了,

因为路径是公共的,结果不会比LCA更优;

然后RMQ处理最小值,再找一下LCA,输出载重量最小值

然后就AC啦

原来程序有BUG(好吧。终于DEBUG出来了。。)

注意!写CMP函数时,如果用>=号会爆栈!

下面贴程序~

 

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,num=0,q;
struct edge{
    int next,to,value;
}g[100010];
int f[10010];
int depth[10010];
int visit[10010];
int fa[10010][20];
int r[50010];
int u[50010],v[50010],w[50010];
int dist [10010][20];
int head[10010];
int cmp(const int a,const int b) {
return w[a]>w[b];}
int getf(int x){
return x==f[x]?x:f[x]=getf(f[x]);}
void addedge(int u,int v,int value){
    g[++num].next=head[u];
    head[u]=num;
    g[num].to=v;
    g[num].value=value;
}
void kruskal(){
    for(int i=1;i<=n;i++) f[i]=i;
    for(int i=1;i<=m;i++) r[i]=i;    
    sort(r+1,r+m+1,cmp);
    for(int i=1;i<=m;i++)
    {
    int x=u[r[i]];
    int y=v[r[i]];
    x=getf(x);y=getf(y);
    if(x!=y){
    addedge(x,y,w[r[i]]);
    addedge(y,x,w[r[i]]);
    f[x]=y;
    }
    }
}
void prelca(int x){
    visit[x]=1;
    for(int i=1;(1<<i)<=depth[x];i++)
    {
        int c=fa[x][i-1];
        fa[x][i]=fa[c][i-1];
        dist[x][i]=min(dist[x][i-1],dist[c][i-1]);    
    }
    for(int i=head[x];i;i=g[i].next)
    {
        int v=g[i].to;
        if(!visit[v])
        {
            depth[v]=depth[x]+1;
            fa[v][0]=x;
            dist[v][0]=g[i].value;
            prelca(v);
        }    
    }
}
int lca(int x,int y)
{
    int rr=2147483647;
    if(depth[x]<depth[y])swap(x,y);
    int dc=depth[x]-depth[y];
    for(int i=0;(1<<i)<=dc;i++)
    if(((1<<i)&dc)&&fa[x][i])
    {
        rr=min(rr,dist[x][i]);
        x=fa[x][i];    

    }    
    if(x==y)return rr;
    for(int i=15;i>=0;i--)
    if(fa[x][i]!=fa[y][i]&&depth[x]>=(1<<i)&&fa[x][i])
    {
        rr=min(rr,min(dist[x][i],dist[y][i]));    
        x=fa[x][i];
        y=fa[y][i];
    }
    return min(rr,min(dist[x][0],dist[y][0])); 
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    scanf("%d%d%d",&u[i],&v[i],&w[i]);
    kruskal();
    for(int i=1;i<=n;i++)
    if(!visit[i])prelca(i);
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        if(getf(a)!=getf(b))printf("-1\n");
        else printf("%d\n",lca(a,b));
    }
    return 0;
}

 

注意:RMQ别打超限,调用空指针会WA的

 

posted @ 2017-05-11 09:56  ghostfly233  阅读(382)  评论(0编辑  收藏  举报