1. 程式人生 > >[BZOJ4016]最短路徑樹問題

[BZOJ4016]最短路徑樹問題

script size operator for oot 所有 top %d head

Description

給一個包含n個點,m條邊的無向連通圖。從頂點1出發,往其余所有點分別走一次並返回。 往某一個點走時,選擇總長度最短的路徑走。若有多條長度最短的路徑,則選擇經過的頂點序列字典序最小的那條路徑(如路徑A為1,32,11,路徑B為1,3,2,11,路徑B字典序較小。註意是序列的字典序的最小,而非路徑中節點編號相連的字符串字典序最小)。到達該點後按原路返回,然後往其他點走,直到所有點都走過。 可以知道,經過的邊會構成一棵最短路徑樹。請問,在這棵最短路徑樹上,最長的包含K個點的簡單路徑長度為多長?長度為該最長長度的不同路徑有多少條? 這裏的簡單路徑是指:對於一個點最多只經過一次的路徑。不同路徑是指路徑兩端端點至少有一個不同,點A到點B的路徑和點B到點A視為同一條路徑。

Input

第一行輸入三個正整數n,m,K,表示有n個點m條邊,要求的路徑需要經過K個點。接下來輸入m行,每行三個正整數Ai,Bi,Ci(1<=Ai,Bi<=n,1<=Ci<=10000),表示Ai和Bi間有一條長度為Ci的邊。數據保證輸入的是連通的無向圖。

Output

輸出一行兩個整數,以一個空格隔開,第一個整數表示包含K個點的路徑最長為多長,第二個整數表示這樣的不同的最長路徑有多少條。

Sample Input

6 6 4
1 2 1
2 3 1
3 4 1
2 5 1
3 6 1
5 6 1

Sample Output

3 4

設$f[i][0/1]$表示到當前分治重心的路徑中點數為$i$的路徑最大長度和方案數

然後註意細節就可以$A$掉它了

代碼:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<queue>
  5 #include<vector>
  6 #include<cstring>
  7 #define M 100010
  8 #define inf 1e9
  9 using namespace std;
10 int n,m,num,ans1,ans2,S,rt,k; 11 int head[M],dis[M],d[M],g[M],size[M],maxn[M],f[M][2]; 12 bool vis[M]; 13 struct point{int to,next,dis;}e[M<<1]; 14 struct edge{int to,dis;}; 15 struct node{int id,v;}; 16 vector<edge>vec[M]; 17 priority_queue<node>Q; 18 bool operator < (node a1,node a2) { 19 return a1.v>a2.v; 20 } 21 void add(int from,int to,int dis) { 22 e[++num].next=head[from]; 23 e[num].to=to; 24 e[num].dis=dis; 25 head[from]=num; 26 } 27 void Dijkstra(int s) { 28 memset(dis,63,sizeof(dis)); 29 dis[s]=0; 30 Q.push((node){s,0}); 31 while(!Q.empty()) { 32 int x=Q.top().id;Q.pop(); 33 if(vis[x]) continue; 34 for(int i=0;i<vec[x].size();i++) { 35 int to=vec[x][i].to; 36 if(dis[to]>dis[x]+vec[x][i].dis) { 37 dis[to]=dis[x]+vec[x][i].dis; 38 Q.push((node){to,dis[to]}); 39 } 40 } 41 } 42 } 43 void build(int x) { 44 vis[x]=true; 45 for(int i=0;i<vec[x].size();i++) { 46 int to=vec[x][i].to; 47 if(vis[to]) continue; 48 if(dis[x]+vec[x][i].dis==dis[to]) { 49 build(to); 50 add(x,to,vec[x][i].dis); 51 add(to,x,vec[x][i].dis); 52 } 53 } 54 } 55 void getroot(int x,int fa) { 56 size[x]=1;maxn[x]=0; 57 for(int i=head[x];i;i=e[i].next) { 58 int to=e[i].to; 59 if(to==fa||vis[to]) continue; 60 getroot(to,x),size[x]+=size[to]; 61 maxn[x]=max(maxn[x],size[to]); 62 } 63 maxn[x]=max(maxn[x],S-size[x]); 64 if(maxn[x]<maxn[rt]) rt=x; 65 } 66 void cal(int x,int fa) { 67 if(d[x]>k) return; 68 if(ans1<g[x]+f[k-d[x]+(d[x]!=k)][0]) ans1=g[x]+f[k-d[x]+(d[x]!=k)][0],ans2=f[k-d[x]+(d[x]!=k)][1]; 69 else if(ans1==g[x]+f[k-d[x]+(d[x]!=k)][0]) ans2+=f[k-d[x]+(d[x]!=k)][1]; 70 for(int i=head[x];i;i=e[i].next) { 71 int to=e[i].to; 72 if(to==fa||vis[to]) continue; 73 d[to]=d[x]+1,g[to]=g[x]+e[i].dis; 74 cal(to,x); 75 } 76 } 77 void insert(int x,int fa) { 78 if(d[x]<=k) { 79 if(f[d[x]][0]<g[x]) f[d[x]][0]=g[x],f[d[x]][1]=1; 80 else if(f[d[x]][0]==g[x]) f[d[x]][1]++; 81 for(int i=head[x];i;i=e[i].next) 82 if(!vis[e[i].to]&&e[i].to!=fa) 83 insert(e[i].to,x); 84 } 85 } 86 void del(int x,int fa) { 87 if(d[x]<=k) { 88 f[d[x]][0]=f[d[x]][1]=-inf; 89 for(int i=head[x];i;i=e[i].next) 90 if(!vis[e[i].to]&&e[i].to!=fa) 91 del(e[i].to,x); 92 } 93 } 94 void solve(int x) { 95 //cout<<x<<endl; 96 vis[x]=true;f[0][0]=0,f[0][1]=1; 97 for(int i=head[x];i;i=e[i].next) { 98 int to=e[i].to; 99 if(vis[to]) continue; 100 d[to]=2,g[to]=e[i].dis,cal(to,x); 101 insert(to,x); 102 } 103 for(int i=head[x];i;i=e[i].next) 104 if(!vis[e[i].to]) 105 del(e[i].to,x); 106 for(int i=head[x];i;i=e[i].next) { 107 int to=e[i].to; 108 if(vis[to]) continue; 109 S=size[to],rt=0,getroot(to,0); 110 solve(rt); 111 } 112 } 113 int main() { 114 scanf("%d%d%d",&n,&m,&k); 115 memset(f,128,sizeof(f)); 116 for(int i=1;i<=m;i++) { 117 int a,b,c;scanf("%d%d%d",&a,&b,&c); 118 vec[a].push_back((edge){b,c}); 119 vec[b].push_back((edge){a,c}); 120 } 121 Dijkstra(1),memset(vis,0,sizeof(vis)),build(1); 122 memset(vis,0,sizeof(vis)),maxn[0]=S=n,getroot(1,0); 123 solve(rt);printf("%d %d",ans1,ans2); 124 return 0; 125 }

[BZOJ4016]最短路徑樹問題