【题解】洛谷P1967货车运输[NOIP2013](同sdoj1807) 最大生成树+lca

82 篇文章 0 订阅
13 篇文章 0 订阅

题目链接
跑一遍最大生成树,把选出来的边连双向边建树,树上跑lca时加一个容量cap[i][j],表示i点向上跳2^j路径最小值
bfs要判重啊啊啊啊啊啊……因为没判重T过好几次了,不长记性
(注释部分读入挂加了直接WA……不知道为什么,太弱了没看出来,有大佬看出来了请讲一下)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std; 
#define INF 0x3f3f3f3f
#define _rep(i,a,b) for(int i=(a);i<=(b);i++)
#define _for(i,a,b) for(int i=(a);i<(b);i++)
typedef long long ll;
const int N=1e4+10;
const int M=5e4+10;
const int DEP=20;
/*const int BUF=40000000;
char Buf[BUF],*buf=Buf;
const int OUT=20000000;
char Out[OUT],*ou=Out;int Outn[30],Outcnt;
inline void write(int x){
  if(!x)*ou++=48;
  else{
    for(Outcnt=0;x;x/=10)Outn[++Outcnt]=x%10+48;
    while(Outcnt)*ou++=Outn[Outcnt--];
  }
}
inline void writell(ll x){
  if(!x)*ou++=48;
  else{
    for(Outcnt=0;x;x/=10)Outn[++Outcnt]=x%10+48;
    while(Outcnt)*ou++=Outn[Outcnt--];
  }
}
inline void writechar(char x){*ou++=x;}
inline void writeln(){*ou++='\n';}
inline void read(int&a){for(a=0;*buf<48;buf++);while(*buf>47)a=a*10+*buf++-48;}
*/int n,m,q;
int head[N],tot,newtot,newhead[N];
struct Edge{
    int u,v,w,nx;
    bool operator<(const Edge&rhs)const{
    return w>rhs.w;}
}edge[M<<1],newedge[M<<1];
inline void addedge(int u,int v,int w)
{
    edge[tot].u=u;
    edge[tot].v=v;
    edge[tot].w=w;
    edge[tot].nx=head[u];
    head[u]=tot++;
}
inline void addnewedge(int u,int v,int w)
{
    newedge[newtot].u=u;
    newedge[newtot].v=v;
    newedge[newtot].w=w;
    newedge[newtot].nx=newhead[u];
    newhead[u]=newtot++;
}
int set[N];
inline int findset(int x){return x==set[x]?x:set[x]=findset(set[x]);}
inline void kruskal()
{
    sort(edge,edge+tot);
    int cnt=0;
    _rep(i,1,n)set[i]=i;
    _for(i,0,tot)
    {
        int u=edge[i].u;
        int v=edge[i].v;
        int w=edge[i].w;
        int fu=findset(u);
        int fv=findset(v);
        if(fu!=fv)
        {
            set[fv]=fu;
            addnewedge(u,v,w);
            addnewedge(v,u,w);
            if(++cnt==n-1)break;
        }
    }
}
bool vis[N];
int dep[N];
int fa[N][DEP];
int cap[N][DEP];
inline void bfs(int u)
{
    queue<int>que;
    while(!que.empty())que.pop();
    dep[u]=0;
    fa[u][0]=u;
    que.push(u);
    while(!que.empty())
    {
        int v=que.front();que.pop();
        if(vis[v])continue;
        vis[v]=1;
        _for(i,1,DEP)
        {
            fa[v][i]=fa[fa[v][i-1]][i-1];
            cap[v][i]=min(cap[v][i-1],cap[fa[v][i-1]][i-1]);
        }
        for(int i=newhead[v];i!=-1;i=newedge[i].nx)
        {
            int to=newedge[i].v;
            if(to==fa[v][0])continue;
            cap[to][0]=newedge[i].w;
            dep[to]=dep[v]+1;
            fa[to][0]=v;
            que.push(to);
        }
    }

}
inline int lca(int u,int v)
{
    int minn=INF;
    if(dep[u]>dep[v])swap(u,v);
    for(int div=dep[v]-dep[u],i=0;div;div>>=1,i++)
    if(div&1)minn=min(minn,cap[v][i]),v=fa[v][i];
    if(v==u)return minn;
    for(int i=DEP-1;i>=0;i--)
    {
        if(fa[u][i]==fa[v][i])continue;
        minn=min(minn,min(cap[v][i],cap[u][i]));
        u=fa[u][i];
        v=fa[v][i];
    }
    minn=min(minn,min(cap[u][0],cap[v][0]));
    return minn;
}
int main()
{
    //fread(Buf,1,BUF,stdin);
    //freopen("in.txt","r",stdin);
    scanf("%d%d",&n,&m);
    int x,y,z;
    memset(head,-1,sizeof(head));
    memset(newhead,-1,sizeof(newhead));
    _rep(i,1,m)
    {
        scanf("%d%d%d",&x,&y,&z);
        addedge(x,y,z);
    }
    kruskal();
    _rep(i,1,n)if(!vis[i])bfs(i);
    scanf("%d",&q);
    _rep(i,1,q)
    {
        scanf("%d%d",&x,&y);
        int fx=findset(x);
        int fy=findset(y);
        if(fx!=fy)
        {
            //writechar('-');
            //write(1);
            //writeln();
            puts("-1");
            continue;
        }
        //write(lca(x,y));
        //writeln();
        printf("%d\n",lca(x,y));
    }
    //fwrite(Out,1,ou-Out,stdout);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值