1. 程式人生 > >藍書(演算法競賽進階指南)刷題記錄——CH0602 黑暗城堡(最短路樹計數)

藍書(演算法競賽進階指南)刷題記錄——CH0602 黑暗城堡(最短路樹計數)

題目大意:給出一張圖,求這張圖不同最短路樹的形態.期中最短路樹指的是對於任意一個點i,樹上1到i的路徑長度等於圖上1到i的最短路徑長度的生成樹.

我們發現這棵生成樹必須滿足的條件其實就是以1為根,1到任意一個點的路徑長度要是原圖的一條最短路.

我們用dis[i]表示原圖上1到i的最短路,那麼若一條邊(x_i,y_i)是最短路上的一條邊並且權值為v_i,那麼必定有dis[x_i]=dis[y_i]+z_idis[y_i]=dis[x_i]+z_i.

那麼我們就可以發現這可樹上的一個父親x_i和兒子y_i就必須滿足dis[y_i]=dis[x_i]+z_i.

考慮一個與prim類似的演算法,我們可以每一次將dis[i]最小的點i加入我們維護的點集.然後我們記錄一個數組cnt[i]表示當前維護點集能夠到達i並且是最短路的邊數.我們發現每一次從任意一個維護的點x連到一個點i滿足dis[x]+v=dis[i]

都是可以的,所以每加入一個點i,我們就把當前的cnt[i]乘到當前答案ans裡,就可以得到答案.

程式碼如下:

#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;
}