題意:給出一顆所有路徑都爲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“左邊”的結點數量和“右邊”的節點數量。
這樣一來,這個問題就分成了下面幾個步驟:
- 計算出每個結點“左邊”的結點數量和“右邊”的節點數量。
- 我們用dfs遞歸到根結點,然後回溯累加即可。
- 計算出某個結點x的答案。
- 使用bfs計算x到其餘結點的距離,累加。
- 通過結點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;
}