題目描述
n 個點 m 條邊的無向圖中,所有點用從 0 開始的 6 位數字串編號,即 000000
、000001
、000002
、……直到 \(n−1\) 對應的 6 位數字串。保證 \(n≤1e6\),所以 6 位的編號不會溢出。
對於除了 000000
以外的每個點,你需要找到一條從 000000
出發且不經過重復點的路徑,使得路徑上所有點的數字串順次連接形成的串的字典序最小。
比較兩個不同的串的字典序的方法是:如果其中某個串是另一個的前綴,則較短的串字典序較小;否則,找出兩個串從左往右掃描時遇到的首個不相等的位置,在這個位置上的數字較小的串字典序較小。
由於輸出路徑過於麻煩,你不需要完整地輸出路徑,只需要將路徑上所有點的數字串視作一個整數,輸出這個數對 998244353
取模的結果。
輸入格式
從標准輸入讀入數據。
第一行輸入兩個整數 \(n\) 和 \(m\)。
第二行輸入一個長度為 \(12m\) 的數字串,依次表示每條邊。每條邊用 12 個數字表示,其中前 6 個與后 6 個數字分別表示這條邊所連接的兩個點的編號。
注意,輸入中可能會包含自環或重邊。
輸出格式
輸出到標准輸出。
輸出 n−1 行,依次輸出除了點 000000
本身以外,點 000000
到每個點的字典序最小的路徑,視為整數后對 998244353
取模的結果。
如果點 000000
不可到達某個點,則在對應的行改為輸出 -1。
樣例1輸入
5 5
000000000003000001000003000001000002000002000000000002000003
樣例1輸出
2000001
2
517560944
-1
樣例1解釋
從 000000
到 000001
所求的路徑對應的串為 000000000002000001
。
從 000000
到 000002
所求的路徑對應的串為 000000000002
。
從 000000
到 000003
所求的路徑對應的串為 000000000002000001000003
,對 998244353
取模后為 517560944
。
從 000000
到 000004
不存在路徑。
子任務
子任務1(11分)
\(1≤n≤1e6,m=0\)。
子任務2(55分)
\(1≤n≤10,0≤m≤20\)。
子任務3(34分)
\(1≤n≤1e6,0≤m≤1e6\)。
剛開始拿到這道題的時候還有點暈,首先我們可以否定直接吧路徑字符串存下來,我們可以把字符串轉化成數來存儲。但是我們就無法比較兩條路徑的字典序先后,但我們又可以發現,一條路徑最優,當且僅當它的每一個路徑上的數都是最小,但不一定是最短,則我們有以下dfs代碼:
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxs=1e7+2e6+100,maxn=1e6+50;
typedef long long ll;
const ll mod=998244353;
vector<int> e[maxn];
int n,m;
char s[maxs];
ll ans[maxn];
inline bool cmp(const int &a,const int &b) {return a<b;}
void dfs(int u,ll dis)
{
ans[u]=dis;
int siz=e[u].size();
for(int i=0;i<siz;i++)
if(ans[e[u][i]]==-1) dfs(e[u][i],(dis*1000000+e[u][i])%mod);
}
int main()
{
int u,v;
scanf("%d%d%s",&n,&m,s);
memset(ans,-1,sizeof(ans));
for(int i=1;i<=m;i++)
{
u=v=0;
for(int j=(i-1)*12;j<i*12-6;j++)
u=(u<<1)+(u<<3)+(s[j]^48);
for(int j=i*12-6;j<i*12;j++)
v=(v<<1)+(v<<3)+(s[j]^48);
if(u==v) continue;
e[u].push_back(v);e[v].push_back(u);
}
for(int i=0;i<n;i++) sort(e[i].begin(),e[i].end(),cmp);
dfs(0,0);
for(int i=1;i<n;i++) printf("%lld\n",ans[i]);
return 0;
}