1. 程式人生 > 其它 >題解 P7100 【[w3R1] 團】

題解 P7100 【[w3R1] 團】

P7100 [w3R1] 團

題目大意:

有一個\(n\)個節點的無向帶權圖,由\(k\)個集合組成,每個集合\(S_i\)中有一些點\(\lbrace(T_1,W_1),(T_2,W_2),...,(T_{\left|S_i\right|},W_{\left|S_i\right|})\rbrace\),集合中的任意兩點都有一條連線\(T_i,T_j\)的無向邊,邊權為\(W_i+W_j\)。我們要求 \(1\)\(i\) 的最短路。

solution:

tf老師說:“圖論題,建完圖後打個模板就行了。”,所以我們考慮如何建圖

暴力地去想,對於每個集合中的點\(T_i\),我們可以從\(T_i\)

向其他點\(T_j\)連一條邊。
根據樣例建圖如下圖:

時間複雜度爆炸:\(O(k|S_i^2|)\)

所以我們考慮,在每個集合中新添一個虛擬節點\(T_{n+i}\),其點權\(W_{i+n}\)為0,再向其他點\(T_j\)連邊。
如下圖:

時間複雜度:\(O(k|S_i|)\)

正確性證明:

我們舉個例子,原圖中\(3\rightarrow4\)的路徑被拆成了\(3\rightarrow7\rightarrow4\),邊權和仍為\(3\)。我們這個過程的實質就是將一條邊拆成兩條邊

接下來,我們打上最短路模板 \(\text{dijkstra}\) 堆優化或者\(\text{SPFA}\)

均可通過此題。

細節處理:

總路徑長有可能超\(\text{int}\),所以要開\(\text{long}\) \(\text{long}\)\(dis\)陣列要初始化成\(\text{0x3f3f3f3f3f3f3f3f}\)

看到這的同學,可以自己去寫程式碼了(tf口吻)

程式碼 ``` cpp #include #include #include using namespace std; typedef long long LL; const int M=800002; int hd[M],nt[M],e[M],ww[M],num=0; bool vis[M]; LL dis[M],INF=0x3f3f3f3f3f3f3f3f; queue Q; void tian(int a,int b,int c) { e[++num]=b; ww[num]=c; nt[num]=hd[a]; hd[a]=num; } void qing() { memset(hd,-1,sizeof(hd)); memset(vis,0,sizeof(vis)); memset(dis,0x3f,sizeof(dis)); } int main() { qing(); int n,k; scanf("%d%d",&n,&k); int ew=n; for(int i=1;i<=k;i++) { int s; scanf("%d",&s); for(int j=1;j<=s;j++) { int t,w; scanf("%d%d",&t,&w); tian(t,ew+i,w);tian(ew+i,t,w); } } dis[1]=0; vis[1]=1; Q.push(1); while(Q.size()) { int x=Q.front(); Q.pop(); vis[x]=0; for(int i=hd[x];i!=-1;i=nt[i]) { int y=e[i],z=ww[i]; if(dis[y]>dis[x]+z) { dis[y]=dis[x]+z; if(!vis[y]) { Q.push(y); vis[y]=1; } } } } for(int i=1;i<=n;i++) printf("%lld ",dis[i]); return 0; } ```

End