藍書(演算法競賽進階指南)刷題記錄——CH0602 黑暗城堡(最短路樹計數)
阿新 • • 發佈:2018-12-16
題目大意:給出一張圖,求這張圖不同最短路樹的形態.期中最短路樹指的是對於任意一個點i,樹上1到i的路徑長度等於圖上1到i的最短路徑長度的生成樹.
我們發現這棵生成樹必須滿足的條件其實就是以1為根,1到任意一個點的路徑長度要是原圖的一條最短路.
我們用dis[i]表示原圖上1到i的最短路,那麼若一條邊是最短路上的一條邊並且權值為,那麼必定有或.
那麼我們就可以發現這可樹上的一個父親和兒子就必須滿足.
考慮一個與prim類似的演算法,我們可以每一次將dis[i]最小的點i加入我們維護的點集.然後我們記錄一個數組cnt[i]表示當前維護點集能夠到達i並且是最短路的邊數.我們發現每一次從任意一個維護的點x連到一個點i滿足
程式碼如下:
#include<bits/stdc++.h> using namespace std; #define Abigail inline void typedef long long LL; const int N=1000,INF=(1<<29)-1; const LL mod=(1<<31)-1; int n,m; struct side{ int y,next,v; }e[N*N+9]; int lin[N+9],top; int dis[N+9],use[N+9]; LL ans=1,cnt[N+9]; struct node{ int x,v; node(int xx,int vv){x=xx;v=vv;} bool operator > (const node &p)const{return v>p.v;} }; priority_queue<node,vector<node>,greater<node> >q; void ins(int x,int y,int v){ e[++top].y=y;e[top].v=v; e[top].next=lin[x]; lin[x]=top; } void dijkstra(int st){ for (int i=1;i<=n;i++) dis[i]=INF; dis[st]=0;q.push(node(st,0)); while (!q.empty()){ int t=q.top().x;q.pop(); if (use[t]) continue; use[t]=1; for (int i=lin[t];i;i=e[i].next) if (dis[t]+e[i].v<dis[e[i].y]){ dis[e[i].y]=dis[t]+e[i].v; q.push(node(e[i].y,dis[e[i].y])); } } } void prim(int st){ for (int i=1;i<=n;i++) use[i]=0; dis[0]=INF;cnt[1]=1LL; for (int i=1;i<=n;i++){ int v=0; for (int j=1;j<=n;j++) if (!use[j]&&dis[v]>dis[j]) v=j; use[v]=1;ans=ans*cnt[v]%mod; for (int j=lin[v];j;j=e[j].next) if (!use[e[j].y]&&dis[v]+e[j].v==dis[e[j].y]) cnt[e[j].y]++; } } Abigail into(){ int x,y,l; scanf("%d%d",&n,&m); for (int i=1;i<=m;i++){ scanf("%d%d%d",&x,&y,&l); ins(x,y,l);ins(y,x,l); } } Abigail work(){ dijkstra(1); prim(1); } Abigail outo(){ printf("%lld\n",ans); } int main(){ into(); work(); outo(); return 0; }