【BZOJ-4016】最短路径树问题 Dijkstra + 点分治
4016: [FJOI2014]最短路径树问题
Time Limit: 5 Sec Memory Limit: 512 MBSubmit: 1092 Solved: 383
[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<=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的路径。
2016.12.7新加数据一组by - wyx-150137
Source
Solution
这道题还是比较好搞的。
首先按照题目的意思搞出 最短路径树 来,这里利用vector排序搞了一下字典序的问题。
然后就可以用 点分治 处理答案了。
令$dp[dep][0/1]$表示经过点数为$dep$的最长路径的长度和方案数,然后DFS一遍就可以了。
Code
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<vector> #include<queue> using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } #define MAXN 100010 #define LL long long int N,M,K; LL Dist,Num; vector<int> G[MAXN]; namespace Graph{ struct EdgeNode{ int next,to,dis,from; }edge[MAXN<<1]; int head[MAXN],cnt; inline void AddEdge(int u,int v,int w) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v; edge[cnt].from=u; edge[cnt].dis=w;} inline void InsertEdge(int u,int v,int w) {AddEdge(u,v,w); AddEdge(v,u,w);} #define Pa pair<int,int> #define MP make_pair #define INF 0x7fffffff priority_queue<Pa,vector<Pa>,greater<Pa> >q; int dist[MAXN]; inline void Dijkstra(int S=1) { for (int i=1; i<=N; i++) dist[i]=INF; q.push(MP(0,S)); dist[S]=0; while (!q.empty()) { int dis=q.top().first; int now=q.top().second; q.pop(); if (dis>dist[now]) continue; for (int i=head[now]; i;i=edge[i].next) { if (dist[edge[i].to]>dis+edge[i].dis) { dist[edge[i].to]=dis+edge[i].dis; q.push(MP(dist[edge[i].to],edge[i].to)); } } } for (int i=1; i<=cnt; i++) { int u=edge[i].from,v=edge[i].to,d=edge[i].dis; if (dist[u]+d==dist[v]) G[u].push_back(v); } // for (int i=1; i<=N; printf("Now=%d\n",i),i++) // for (int j=0; j<G[i].size(); j++) // printf("%d ",G[i][j]); } } namespace TreeDivide{ struct EdgeNode{ int next,to,dis; }edge[MAXN<<1]; int head[MAXN],cnt; inline void AddEdge(int u,int v,int w) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v; edge[cnt].dis=w;} inline void InsertEdge(int u,int v,int w) {/*printf("<%d %d>\n",u,v,w);*/ AddEdge(u,v,w); AddEdge(v,u,w);} int size[MAXN],mx[MAXN],root,Sz; bool visit[MAXN]; inline void Getroot(int now,int last) { size[now]=1; mx[now]=0; for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=last && !visit[edge[i].to]) { Getroot(edge[i].to,now); size[now]+=size[edge[i].to]; mx[now]=max(mx[now],size[edge[i].to]); } mx[now]=max(mx[now],Sz-size[now]); if (mx[now]<mx[root]) root=now; } int f[MAXN][2],g[MAXN][2]; inline void DFS(int now,int last,int dep,int dis) { if (dep>K) return; if (dis>f[dep][0]) f[dep][0]=dis,f[dep][1]=1; else if (dis==f[dep][0]) f[dep][1]++; for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=last && !visit[edge[i].to]) { DFS(edge[i].to,now,dep+1,dis+edge[i].dis); } } inline void Divide(int now) { visit[now]=1; for (int i=1; i<=K; i++) g[i][0]=g[i][1]=0; g[0][0]=0; g[0][1]=1; K--; for (int i=head[now]; i; i=edge[i].next) if (!visit[edge[i].to]) { for (int j=1; j<=K; j++) f[j][0]=f[j][1]=0; DFS(edge[i].to,now,1,edge[i].dis); for (int j=1; j<=K; j++) { if (Dist<g[K-j][0]+f[j][0]) Dist=g[K-j][0]+f[j][0],Num=(LL)g[K-j][1]*f[j][1]; else if (Dist==g[K-j][0]+f[j][0]) Num+=(LL)g[K-j][1]*f[j][1]; } for (int j=1; j<=K; j++) { if (g[j][0]<f[j][0]) g[j][0]=f[j][0],g[j][1]=f[j][1]; else if (g[j][0]==f[j][0]) g[j][1]+=f[j][1]; } } K++; for (int i=head[now]; i; i=edge[i].next) if (!visit[edge[i].to]) { root=0; Sz=size[edge[i].to]; Getroot(edge[i].to,now); Divide(root); } } bool mark[MAXN]; inline void BuildTree(int now) { mark[now]=1; sort(G[now].begin(),G[now].end()); for (int i=0; i<G[now].size(); i++) if (!mark[G[now][i]]) { BuildTree(G[now][i]); InsertEdge(now,G[now][i],Graph::dist[G[now][i]]-Graph::dist[now]); } } }using namespace TreeDivide; int main() { N=read(),M=read(),K=read(); for (int i=1,x,y,z; i<=M; i++) { x=read(),y=read(),z=read(); Graph::InsertEdge(x,y,z); } Graph::Dijkstra(); TreeDivide::BuildTree(1); Sz=mx[root=0]=N; TreeDivide::Getroot(1,0); TreeDivide::Divide(root); printf("%lld %lld\n",Dist,Num); return 0; }
——It's a lonely path. Don't make it any lonelier than it has to be.