bzoj 4016 [FJOI2014]最短路径树问题(最短路径树+树分治)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 4016 [FJOI2014]最短路径树问题(最短路径树+树分治)相关的知识,希望对你有一定的参考价值。
4016: [FJOI2014]最短路径树问题
Time Limit: 5 Sec Memory Limit: 512 MBSubmit: 426 Solved: 147
[Submit][Status][Discuss]
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&
lt;=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
1 2 1
2 3 1
3 4 1
2 5 1
3 6 1
5 6 1
Sample Output
3 4
HINT
对于所有数据n<=30000,m<=60000,2<=K<=n。数据保证最短路径树上至少存在一条长度为K的路径。
【思路】
先求出满足要求的最短路树来。
分治:求出过根节点的点对数,其它递归处理。
求过根节点的点对:假设现在处理根节点的S子树,用f[i][0]表示前S-1棵子树中与根相距i个节点(不含根)的最长路径,f[i][1]表示方案数,类似的定义tmp为当前S子树的统计结果。一遍bfs构造出tmp,枚举该子树上的结点数更新答案,然后用tmp更新f。
需要注意的有:
累计答案的诸多小细节。
f[][],tmp[][],queue的清零。
【代码】
1 #include<cstdio> 2 #include<vector> 3 #include<queue> 4 #include<cstring> 5 #include<iostream> 6 #include<algorithm> 7 using namespace std; 8 9 const int N = 30000+10; 10 const int INF = 1e9+1e9; 11 12 struct Edge { 13 int v,w; 14 Edge(int v=0,int w=0):v(v),w(w) {} 15 bool operator < (const Edge& rhs) const { 16 return v<rhs.v; 17 } 18 }; 19 queue<int> q; 20 int n,m,K,root,size,d[N],inq[N]; 21 int ans1,ans2,siz[N],mx[N],vis[N],dep[N],dis[N],fa[N]; 22 vector<Edge> g[N],G[N]; 23 24 void spfa() { 25 for(int i=2;i<=n;i++) d[i]=INF; 26 memset(inq,0,sizeof(inq)); 27 inq[1]=1; q.push(1); 28 while(!q.empty()) { 29 int u=q.front(); q.pop(); inq[u]=0; 30 for(int i=0;i<g[u].size();i++) { 31 int v=g[u][i].v,w=g[u][i].w; 32 if(d[v]>d[u]+w) { 33 d[v]=d[u]+w; 34 if(!inq[v]) inq[v]=1,q.push(v); 35 } 36 } 37 } 38 } 39 void dfs(int u) { 40 vis[u]=1; 41 for(int i=0;i<g[u].size();i++) { 42 int v=g[u][i].v,w=g[u][i].w; 43 if(!vis[v] && d[v]==d[u]+w) { 44 G[u].push_back(Edge(v,w)); 45 G[v].push_back(Edge(u,w)); 46 dfs(v); 47 } 48 } 49 } 50 void getroot(int u) { 51 siz[u]=1; mx[u]=0; 52 for(int i=0;i<G[u].size();i++) { 53 int v=G[u][i].v; 54 if(v!=fa[u] && !vis[v]) { 55 fa[v]=u; 56 getroot(v); 57 siz[u]+=siz[v]; 58 if(siz[v]>mx[u]) mx[u]=siz[v]; 59 } 60 } 61 mx[u]=max(mx[u],size-siz[u]); 62 if(mx[u]<mx[root]) root=u; 63 } 64 int f[N][2],tmp[N][2]; 65 void solve(int u,int S){ 66 vis[u]=1; f[0][1]=1; 67 int m=G[u].size(); 68 for(int i=0;i<m;i++) { 69 int v=G[u][i].v; 70 if(!vis[v]) { 71 while(!q.empty()) q.pop(); //clear 72 q.push(v),dep[v]=1,dis[v]=G[u][i].w,fa[v]=u; 73 while(!q.empty()) { 74 int now=q.front(); q.pop(); 75 int k=dep[now]; 76 if(k>K) break; 77 if(dis[now]>tmp[k][0]) 78 tmp[k][0]=dis[now],tmp[k][1]=0; 79 if(dis[now]==tmp[k][0]) tmp[k][1]++; 80 for(int j=0;j<G[now].size();j++) { 81 int to=G[now][j].v; 82 if(!vis[to] && to!=fa[now]) { 83 fa[to]=now; 84 dep[to]=dep[now]+1; 85 dis[to]=dis[now]+G[now][j].w; 86 q.push(to); 87 } 88 } 89 } 90 //for(int j=1;j<=K;j++) printf("(%d,%d) ",tmp[j][0],tmp[j][1]);cout<<endl; 91 for(int j=1;j<=K;j++) { //tmp位于前可以取[1..K] 92 if(tmp[j][0]+f[K-j][0]>ans1) 93 ans1=tmp[j][0]+f[K-j][0],ans2=0; 94 if(tmp[j][0]+f[K-j][0]==ans1) 95 ans2+=tmp[j][1]*f[K-j][1]; 96 } 97 for(int j=1;j<=K;j++) { 98 if(tmp[j][0]>f[j][0]) f[j][0]=tmp[j][0],f[j][1]=0; 99 if(tmp[j][0]==f[j][0]) f[j][1]+=tmp[j][1]; 100 tmp[j][1]=tmp[j][0]=0; 101 } 102 } 103 } 104 //cout<<u<<": "<<ans1<<" "<<ans2<<endl; 105 for(int j=0;j<=K;j++) f[j][0]=f[j][1]=0; 106 m=G[u].size(); 107 for(int i=0;i<m;i++) { 108 int v=G[u][i].v; 109 if(!vis[v]) { 110 size=siz[v]; 111 if(siz[v]>siz[u]) siz[v]=S-siz[v]; 112 root=0; 113 if(size>=K) getroot(v); 114 solve(root,siz[v]); 115 } 116 } 117 } 118 void read(int& x) { 119 char c=getchar(); int f=1; x=0; 120 while(!isdigit(c)){if(c==‘-‘) f=-1;c=getchar();} 121 while(isdigit(c)) x=x*10+c-‘0‘,c=getchar(); 122 x*=f; 123 } 124 int main() { 125 //freopen("in.in","r",stdin); 126 //freopen("out.out","w",stdout); 127 read(n),read(m),read(K); K--; 128 int u,v,w; 129 for(int i=0;i<m;i++) { 130 read(u),read(v),read(w); 131 g[u].push_back(Edge(v,w)); 132 g[v].push_back(Edge(u,w)); 133 } 134 for(int i=1;i<=n;i++) 135 sort(g[i].begin(),g[i].end()); 136 spfa(); 137 memset(vis,0,sizeof(vis)); 138 dfs(1); 139 size=n; mx[0]=INF; root=0; 140 memset(vis,0,sizeof(vis)); 141 getroot(1) , solve(root,size); 142 printf("%d %d",ans1,ans2); 143 return 0; 144 }
以上是关于bzoj 4016 [FJOI2014]最短路径树问题(最短路径树+树分治)的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ 4016: [FJOI2014]最短路径树问题( 最短路 + 点分治 )