Gym - 100676H(树的直径+边连通分量板子)

题意:T组数据。n个点 m条边。

无向图。给你a,b,c a-b相连权值为c得到一个无向图。你可以指定一个点为主城市。与这个点相连的所有城市的边权为0,,然后问离这个点的最远距离是多少。

做法:肯定是一个连通分量吧。然后边双连通缩点以后就是一棵树,找树上的直径,首都一定是直径上的点。

直径一定是这棵树上最长的距离。

求树直径做法:先随便找一个点然后一直dfs。一直deep+1到不能再dfs为止。那么这个点肯定是树的一个端点。再让st=这个端点。

反向dfs一次。再找到的一个点肯定是树的另一个端点。那么我已经找到树的两端。再dfs一次把整个树连通。

//china no.1
#include <vector>
#include <iostream>
#include <string>
#include <map>
#include <stack>
#include <cstring>
#include <queue>
#include <list>
#include <stdio.h>
#include <set>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <iomanip>
#include <cctype>
#include <sstream>
#include <functional>
#include <stdlib.h>
#include <time.h>
#include <bitset>
using namespace std;

#define pi acos(-1)
#define endl '\n'
#define srand() srand(time(0));
#define me(x) memset(x,0,sizeof(x));
#define foreach(it,a) for(__typeof((a).begin()) it=(a).begin();it!=(a).end();it++)
#define close() ios::sync_with_stdio(0); cin.tie(0);
#define FOR(x,n,i) for(int i=x;i<=n;i++)
#define FOr(x,n,i) for(int i=x;i<n;i++)
#define W while
#define sgn(x) ((x) < 0 ? -1 : (x) > 0)
#define bug printf("***********\n");
typedef long long LL;
const int INF=0x3f3f3f3f;
const LL LINF=0x3f3f3f3f3f3f3f3fLL;
const int dx[]={-1,0,1,0,1,-1,-1,1};
const int dy[]={0,1,0,-1,-1,1,-1,1};
const int maxn=1e3+10;
const int maxx=2e5+100;
const double EPS=1e-7;
const int MOD=10000007;
template<class T>inline T min(T a,T b,T c) { return min(min(a,b),c);}
template<class T>inline T max(T a,T b,T c) { return max(max(a,b),c);}
template<class T>inline T min(T a,T b,T c,T d) { return min(min(a,b),min(c,d));}
template<class T>inline T max(T a,T b,T c,T d) { return max(max(a,b),max(c,d));}

int vis[maxx],dfn[maxx],low[maxx],bel[maxx],num[maxx],res,pre[maxx];
int cont=1,n,m;
struct node
{
    int u,v;
    LL w;
}s[maxx];
vector<int>G[maxx],G_new[maxx];
queue<int>Q;
stack<int>S;
vector<node>g[maxx];

LL dis1[maxx],dis2[maxx];
LL max_len;int st,ed;
void tarjan(int fa,int x)
{
    dfn[x]=low[x]=cont++;
    S.push(x);
    vis[x]=1;
    int len=G[x].size();
    for(int i=0; i<len; i++)
    {
        int nex=G[x][i];
        if(fa==nex) continue;
        if(!vis[nex])
        {
            tarjan(x,nex);
            low[x]=min(low[x],low[nex]);
        }
        else if(vis[nex]==1)
            low[x]=min(low[x],dfn[nex]);
    }
    if(dfn[x]==low[x])
    {
        res++;
        while(1)
        {
            int t=S.top();
            S.pop();
            bel[t]=res;
            vis[t]=2;
            pre[res]=min(pre[res],t);//pre[缩点之前的点]
            if(x==t)break;
        }
    }
}


void init()
{
    me(pre);me(vis);(low);me(bel);me(dfn);me(num);
    for(int i=1;i<=n;i++)
    {
        G[i].clear();
        pre[i]=INF;
        g[i].clear();
    }
    me(s);
    res=0;cont=1;
}

void dd(int u,int fa,LL len,int flag)//树的直径
{
    if(flag) dis1[u]=len;
    if(!flag) dis2[u]=len;
    if(flag&&len>max_len) st=u,max_len=len;
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i].v;
        if(v==fa) continue;
        dd(v,u,len+g[u][i].w,flag);
    }
}

void solve()
{
    cin>>n>>m;
    init();
    for(int i=1;i<=m;i++)
    {
        int x,y;LL z;
        cin>>x>>y>>z;
        s[i].u=x;s[i].v=y;s[i].w=z;
        G[x].push_back(y);
        G[y].push_back(x);
    }
    tarjan(-1,1);
    for(int i=1;i<=m;i++)
    {
        int u=bel[s[i].u],v=bel[s[i].v];//缩点之后的树
        LL w=s[i].w;
        if(u!=v)
        {
            g[u].push_back(node{0,v,w});
            g[v].push_back(node{0,u,w});
        }
    }
    me(dis1);me(dis2);
    max_len=-1;
    dd(1,-1,0,1);
    ed=st;
    max_len=-1;
    dd(st,-1,0,1);
    swap(st,ed);
    dd(ed,-1,0,0);
    LL maxc=LINF;
    int num=0;
    for(int i=1;i<=res;i++)
    {
        if(max(dis1[i],dis2[i])<maxc)
        {
            maxc=max(dis1[i],dis2[i]);
            num=pre[i];
        }
        if(max(dis1[i],dis2[i])==maxc)
            num=min(pre[i],num);
    }
    cout<<num<<" "<<maxc<<endl;
}

int main()
{
    int t;
    //t=1;
    close();
    cin>>t;
    for(int cas=1;cas<=t;cas++)
        solve();
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值