noip2013day1题解

转圈游戏:

# include <iostream>
# include <algorithm>
# include <string>
# include <cstring>
# include <cmath>
# include <ctime>
# include <cstdio>
# include <queue>
using namespace std; 
typedef long long ll;
long long n,m,k,x;  
ll Read()
{
	ll i=0,f=1;char c=getchar();
	whi

#include<iostream> 
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

#define MAXN 10010
#define MAXM 50010
#define MAXD 22
#define inf 200000

int father[MAXN],n,m,q;
bool visit[MAXN];
int up[MAXN][MAXD],Min[MAXN][MAXD],h[MAXN];

struct saver
{
    int s,t,d;
} e[MAXM];
 
int cmp(saver a,saver b)
{
    return a.d>b.d;
}
 
//并查集
void init()   
{
    for(int i=0; i<MAXN; i++)father[i]=i;
}

int Find(int x)
{
    if(x==father[x])return father[x];
    else
    {
        father[x]=Find(father[x]);
        return father[x];
    }
}

void Union(int x,int y)
{
    int fx=Find(x);
    int fy=Find(y);
    if(fx!=fy)
    {
        father[fx]=fy;
    }
}

//处理最大生成树后的边的关系
struct edge
{
    edge *next;
    int t,d;
    edge()
    {
        next=NULL;
    }
}*head[MAXN];

void Add(int s,int t,int d)
{
    edge *p=new(edge);
    p->t=t,p->d=d,p->next=head[s];
    head[s]=p;
}

void AddEdge(int s,int t,int d)
{
    Add(s,t,d);
    Add(t,s,d);
}

//一个dfs过程,对于和v在一棵树上的所有结点计算其在树中的高度以及up[i][0]和Min[i][0]
void BuildTree(int v)
{
    visit[v]=true;
    for(edge *p=head[v]; p; p=p->next)
    {
        if(!visit[p->t])
        {
            up[p->t][0]=v;
            Min[p->t][0]=p->d;
            h[p->t]=h[v]+1;
            BuildTree(p->t);
        }
    }
}

//得到u和v两分支高度相同的节点,在此基础上开始倍增
int Move(int &v,int H)
{
    int rec = inf;
    for(int i=MAXD-1; i>=0; i--)
    {
        if(h[up[v][i]]>=H)
        {
            rec=min(rec,Min[v][i]);
            v=up[v][i];
        }
    }
    return rec;
}

//倍增算法实现询问u和v路径上最小值
int Query(int u,int v)
{
    if(Find(u)!=Find(v))return -1;
    int rec = inf;
    if(h[u]!=h[v])rec=h[u]>h[v]?Move(u,h[v]):Move(v,h[u]);
    if(u==v) return rec;
    for(int i=MAXD-1; i>=0; i--)
    {
        if(up[u][i]!=up[v][i])
        {
            rec=min(rec,min(Min[u][i],Min[v][i]));
            u=up[u][i];
            v=up[v][i];
        }
    }
    rec=min(rec,min(Min[u][0],Min[v][0]));
    return rec;
}

int main()
{ 
    //输入
   	//freopen("truck.in", "r", stdin);
    //freopen("truck.out", "w", stdout);
    memset(head,0,sizeof(head));
    memset(up,0,sizeof(up));
    scanf("%d %d",&n,&m);
    for(int i=0; i<m; i++)scanf("%d %d %d",&e[i].s,&e[i].t,&e[i].d);
    
    //排序后生产最大生成树构成的森林
    sort(e,e+m,cmp);
    init();
    for(int i=0; i<m; i++)
    {
        if(Find(e[i].s)!=Find(e[i].t))
        {
            Union(e[i].s,e[i].t);
            AddEdge(e[i].s,e[i].t,e[i].d);
        }
    }
    
    //初始化每一个结点的up[i][0]和Min[i][0],并计算结点在树中高度
    memset(visit,false,sizeof(visit));
    for(int i=1; i<=n; i++)
    {
        if(!visit[i])
        {
            h[i]=0;
            BuildTree(i);
            Min[i][0]=inf;
            up[i][0]=i;
        }
    }
    
    //预处理出up[i][j]和Min[i][j]数组
    for(int i=1; i<MAXD; i++)
    {
        for(int j=1; j<=n; j++)
        {
            up[j][i]=up[up[j][i-1]][i-1];
            Min[j][i]=min(Min[j][i-1],Min[up[j][i-1]][i-1]);
        }
    }
    
    //处理询问
    scanf("%d",&q);
    while(q--)
    {
        int u,v;
        scanf("%d %d",&u,&v);
        printf("%d\n",Query(u,v));
    }
    return 0;
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值