Codeforces 1209F Koala and Notebook 题解 [ 紫 ] [ 最短路 ] [ 拆边 ] [ 分层图 ]
神秘题。
最小化数字的条件可以拆成:
- 先最小化数字长度。
- 在此基础上,最小化字典序。
直接建长度的最短路 DAG,然后在 DAG 上贪显然是不对的,因为对于长度不同的两个数字我们没办法判断接哪个更优秀。于是考虑一个图论建模的常用技巧:拆边。我们把 \(w\) 的每个数位拆成一个新的边,容易发现最后最多只会拆成 \(\lg w\) 条,因此是可以接受的。
然后直接跑 BFS,优先跑小的边,对,吗?我们考虑一个 hack:假设我有一堆数在队列里,这些数的答案的前缀完全相同,但是接下来他们的出边的边权不同。\(u\to v\) 的边权为 \(a\),\(u'\to v\) 的边权为 \(b\),如果此时 \(u\) 比 \(u'\) 先出队,且 \(a>b\),那么显然是不优秀的。
因此我们需要将这些答案前缀相同的节点一起更新,然后从 \(0\sim 9\) 依次枚举他们的出边,按照正常做 BFS 即可。具体实现既可以像其他题解一样用 queue 里开 vector,也可以像我一样对每个答案前缀相同的段直接开 vector。时间复杂度 \(O(n\lg w)\)。
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
const int N = 1100005;
const ll mod = 1e9 + 7;
vector<int> g[N][10], q[N];
int n, m, cnt;
ll dis[N];
bitset<N> vis;
int main()
{
//freopen("sample.in", "r", stdin);
//freopen("sample.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
cnt = n;
for(int i = 1; i <= m; i++)
{
int u, v;
cin >> u >> v;
int now = i, pre = v;
while(now >= 10)
{
g[++cnt][now % 10].push_back(pre);
pre = cnt;
now /= 10;
}
g[u][now].push_back(pre);
now = i, pre = u;
while(now >= 10)
{
g[++cnt][now % 10].push_back(pre);
pre = cnt;
now /= 10;
}
g[v][now].push_back(pre);
}
q[1].push_back(1);
vis[1] = 1;
int T = 1;
for(int t = 1; t <= T; t++)
{
for(int i = 0; i < 10; i++)
{
bool flag = 0;
for(auto u : q[t])
{
for(auto v : g[u][i])
{
if(vis[v]) continue;
dis[v] = (dis[u] * 10 + i) % mod;
q[T + 1].push_back(v);
vis[v] = flag = 1;
}
}
if(flag) T++;
}
}
for(int i = 2; i <= n; i++) cout << dis[i] << "\n";
return 0;
}

浙公网安备 33010602011771号