牛客病毒感染

B.病毒感染

题意:给出一颗所有路径都为1的树,让你找出到其余点距离和最小的点,若存在多个,都输出。

暴力法:跑n边迪杰斯特拉,n>=5e5,显然不行。

试着画图模拟找思路:

在这里插入图片描述

上图中,1到其余各点的距离和为7(1+1+1+1+1+2),2到其余各点的距离和为10(1+1+2+2+2+2),因为题目保证了各路径长度都为1,显然,从1到2的过程中,增加的路径是1、3、4、5、6五个点贡献的,减少的路径则是2、7两个点贡献的。也就是ans[2]=ans[1]+5-2其中的5和2,就是结点2“左边”的结点数量和“右边”的节点数量。

这样一来,这个问题就分成了下面几个步骤:

  1. 计算出每个结点“左边”的结点数量和“右边”的节点数量。
  • 我们用dfs递归到根结点,然后回溯累加即可。
  1. 计算出某个结点x的答案。
  • 使用bfs计算x到其余结点的距离,累加。
  1. 通过结点x来递推其余结点 。
  • 使用bfs转移。

AC代码:

/*
 * @Author: hesorchen
 * @Date: 2020-04-14 10:33:26
 * @LastEditTime: 2020-07-01 15:49:29
 * @Link: https://hesorchen.github.io/
 */
#include <bits/stdc++.h>
using namespace std;

#define INF 0x3f3f3f3f
#define pll pair<long long, long long>
#define ll long long
#define IOS                      \
    ios::sync_with_stdio(false); \
    cin.tie(0);                  \
    cout.tie(0);

ll head[500010], ct = 1;
struct node
{
    ll v, w, next;
} G[500010];
void add(ll u, ll v)
{
    G[ct].v = v;
    G[ct].w = 1;
    G[ct].next = head[u];
    head[u] = ct++;
}

ll cnt[500010];
ll dis[500010];
bool vis[500010];

void dfs1(ll s) //计算出每个结点“左边”的结点数量和“右边”的节点数量
{
    cnt[s] = 1;
    for (int i = head[s]; i; i = G[i].next)
    {
        if (vis[G[i].v])
            continue;
        vis[G[i].v] = 1;
        dfs1(G[i].v);
        cnt[s] += cnt[G[i].v];
        vis[G[i].v] = 0;
    }
}

pll a;
queue<pll> q;
void bfs(ll s, ll sum) //bfs求出结点1到其余各点的距离
{
    vis[s] = 1;
    q.push(make_pair(s, sum));
    while (!q.empty())
    {
        ll pos = q.front().first;
        ll sum = q.front().second;
        q.pop();
        for (int i = head[pos]; i; i = G[i].next)
        {
            if (!vis[G[i].v])
            {
                vis[G[i].v] = 1;
                dis[G[i].v] = sum + 1;
                q.push(make_pair(G[i].v, sum + 1));
            }
        }
    }
}
ll n, m;
void bfs2(ll pos, ll sum) //递推
{
    vis[pos] = 1;
    dis[pos] = sum;
    q.push(make_pair(pos, 5201314)); //...只需一个pos参数(不想新建queue了
    while (!q.empty())
    {

        ll pos = q.front().first;
        ll sum = q.front().second;
        q.pop();
        for (int i = head[pos]; i; i = G[i].next)
        {
            if (vis[G[i].v])
                continue;
            vis[G[i].v] = 1;
            dis[G[i].v] = dis[pos] - cnt[G[i].v] + (n - cnt[G[i].v]); //状态转移
            q.push(make_pair(G[i].v, 5201314));
        }
    }
}
int main()
{

    cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        ll u, v;
        cin >> u >> v;
        add(u, v);
        add(v, u);
    }
    vis[1] = 1;
    dfs1(1);
    fill(vis, vis + 500010, 0);
    bfs(1, 0); //bfs求出结点1到其余各点的距离和,使用结点1递推其他点
    ll sum_1 = 0;
    for (int i = 1; i <= n; i++)
        sum_1 += dis[i];
    fill(vis, vis + 500010, 0);
    fill(dis, dis + 500010, 0);
    bfs2(1, sum_1); //递推

    ll minn = INF;
    for (int i = 1; i <= n; i++)
        minn = min(minn, dis[i]);
    for (int i = 1; i <= n; i++)
        if (minn == dis[i])
            cout << i << ' ';
    cout << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hesorchen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值