题解:P3547 [POI 2013] CEN-Price List
题目链接:P3547 [POI 2013] CEN-Price List
:::info[推导过程]
首先,读完题我们可以发现这道题可以转换为在原图的铁路边和满足条件的飞机边构成的新图中,求从
而我们容易发现,最短路径由以下三种情况构成:
- 整条路径全部由铁路构成。
- 整条路径由若干条铁路和若干条飞机边构成。
- 整条路径全部由飞机边构成。
对于前面两种情况,我们只需要在原图上用 BFS 跑一遍最短路。容易发现如果使用飞机边那么肯定代表相邻两条铁路边的花费大于飞机边的花费,为了使花费最少,我们应当尽可能使用飞机边。所以前两种情况只需要跑一遍最短路,然后对于每个点
但对于第三种情况,如果直接枚举每个点的所有两步路径来建飞机边,显然复杂度可能达到
对于不优化的情况,我们仍然需要在图上跑 BFS。我们观察到 BFS 有一个很重要的性质:当一个点第一次被访问时,此时得到的就是它在飞机图中的最短距离。这个性质很显然,就不再证明了。于是我们可以思考从这个性质入手,看如何优化。
具体地,我们假设当前正在处理点
找到的。
那么:
如果之后又从另一个点
因此:
也就是说,第二次找到
既然以后再也不需要通过新的起点更新
题目中定义我们枚举路径
对于这种不成立的情况,我们可以观察到
但通过对于三元环计数的模板题:P1989这道题的学习,我们可以发现一个性质:在一个简单无向图中,三元环的数量不会超过
按照三元环计数中的思想,我们把所有边按照度数从小到大定向,度数相同按照编号从小到大定向。这样我们可以保证每个点的出度不超过
对于一张无向图,每条无向边都会给两个端点各贡献一次度数,所以每条边贡献
因此:
- 对于度数小于
\sqrt{2m} 的点,其出度也必然小于\sqrt{2m} 。 - 我们假设度数大于等于
\sqrt{2m} 的点的数量为cnt 。他们的度数之和至少是cnt \cdot \sqrt{2m} 。而一张无向图中只有2m 条边,因此需要满足cnt \times \sqrt{2m} \le 2m \Rightarrow cnt \le \sqrt{2m} 。
综上,一张无向图中所有点的出度不超过
于是枚举一条边,再枚举端点的出边,复杂度就是
我们发现这个复杂度我们是可以接受的,于是这道题就这样解决了。
:::
:::info[复杂度分析]{open}
- 第一次 BFS 的复杂度为:
O(n+m) 。 - 第二次 BFS 的复杂度为:
O(n+m\sqrt{m}) 。
因此总时间复杂度为:
空间复杂度为
| 可以通过这道题。 |
|---|
:::success[参考代码]
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5 + 10;
const int M = 1e5 + 10;
int n, m, k, a, b;
int head[N], idx;
int head2[N], idx2;
struct node
{
int to, nxt;
}e[M << 1], e2[M << 1];
int dist[N], flag[N];
bool vis[N];
ll ans[N];
inline void add(int u, int v)
{
e[idx].to = v;
e[idx].nxt = head[u];
head[u] = idx++;
}
inline void add2(int u, int v)
{
e2[idx2].to = v;
e2[idx2].nxt = head2[u];
head2[u] = idx2++;
}
inline void bfs1()
{
queue <int> q; q.push(k); dist[k] = 0; vis[k] = true;
while (!q.empty())
{
auto u = q.front(); q.pop();
for (int i = head[u]; ~i; i = e[i].nxt)
{
int v = e[i].to;
if (vis[v]) continue;
dist[v] = dist[u] + 1;
vis[v] = true;
q.push(v);
}
}
for (int i = 1; i <= n; ++i)
{
ans[i] = min((ll)dist[i] * a, (ll)dist[i] / 2 * b + (ll)dist[i] % 2 * a);
}
}
inline void bfs2()
{
memset(vis, false, sizeof vis);
memset(dist, 0, sizeof dist);
queue <int> q; q.push(k); vis[k] = true;
while (!q.empty())
{
auto u = q.front(); q.pop();
for (int i = head[u]; ~i; i = e[i].nxt)
{
int v = e[i].to;
flag[v] = u;
}
for (int i = head[u]; ~i; i = e[i].nxt)
{
int v = e[i].to;
int pre = -1;
for (int j = head2[v]; ~j; j = e2[j].nxt)
{
int z = e2[j].to;
if (flag[z] != u)
{
if (pre == -1) head2[v] = e2[j].nxt;
else e2[pre].nxt = e2[j].nxt;
if (vis[z]) continue;
dist[z] = dist[u] + 1;
q.push(z);
vis[z] = true;
}
else pre = j;
}
}
}
for (int i = 1; i <= n; ++i)
{
if (!vis[i]) continue;
ans[i] = min(ans[i], (ll)dist[i] * b);
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n >> m >> k >> a >> b;
memset(head, -1, sizeof head);
memset(head2, -1, sizeof head2);
for (int i = 1; i <= m; ++i)
{
int u, v; cin >> u >> v;
add(u, v); add(v, u);
add2(u, v); add2(v, u);
}
bfs1(); bfs2();
for (int i = 1; i <= n; ++i) cout << ans[i] << endl;
return 0;
}
:::