Codeforces 1209F Koala and Notebook 题解 [ 紫 ] [ 最短路 ] [ 拆边 ] [ 分层图 ]

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;
}
posted @ 2025-11-19 16:05  KS_Fszha  阅读(0)  评论(0)    收藏  举报