BZOJ4016 最短路径树问题
给一个包含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
Hint
对于所有数据n<=30000,m<=60000,2<=K<=n。
数据保证最短路径树上至少存在一条长度为K的路径。
2016.12.7新加数据一组by - wyx-150137
题解
首先我们先建立最短路径树(dijkstra+dfs),让后对最短路径树做点分治,f[i][j][2]:表示前i个子树到根节点距离为j {0:表示最大距离 1:表示当前最大距离的路径数}
对与新加的子树,做一边相同操作后,更新f即可.
参考代码
#include<bits/stdc++.h> using namespace std; #define PI acos(-1.0) #define pii pair<int,int> #define mkp make_pair typedef long long ll; const int INF=0x3f3f3f3f; const int maxn=60010; int n,m,k; vector<pii> v[maxn]; int head[maxn],h[maxn],vis[maxn],fa[maxn],cnt1,cnt; int f[maxn][2],g[maxn][2]; int dis[maxn],deep[maxn]; int rt,sum,S,mx[maxn],size[maxn]; int ans1,ans2; struct Edge{ int v,w,nxt; } edge[maxn<<1],e[maxn<<1]; void addedge(int u,int v,int w) { edge[cnt].v=v; edge[cnt].w=w; edge[cnt].nxt=head[u]; head[u]=cnt++; } void addedge1(int u,int v,int w) { e[cnt1].v=v; e[cnt1].w=w; e[cnt1].nxt=h[u]; h[u]=cnt1++; } void dijkstra() { priority_queue<pii,vector<pii>,greater<pii> > q; q.push(make_pair(0,1)); for(int i=2;i<=n;++i) dis[i]=INF; dis[1]=0; while(!q.empty()) { int u=q.top().second; int x=q.top().first;q.pop(); if(dis[u]!=x) continue; for(int i=head[u];~i;i=edge[i].nxt) { int V=edge[i].v; if(dis[u]+edge[i].w<dis[V]) { dis[V]=dis[u]+edge[i].w; q.push(make_pair(dis[V],V)); } } } } void dfs(int u) { vis[u]=1; for(int i=head[u];~i;i=edge[i].nxt) { int v=edge[i].v; if(!vis[v] && dis[u]+edge[i].w==dis[v]) { addedge1(u,v,edge[i].w); addedge1(v,u,edge[i].w); dfs(v); } } } void getroot(int u) { size[u]=1;mx[u]=0; for(int i=h[u];~i;i=e[i].nxt) { int v=e[i].v; if(v!=fa[u] && !vis[v]) { fa[v]=u; getroot(v); mx[u]=max(mx[u],size[v]); size[u]+=size[v]; } } mx[u]=max(mx[u],sum-size[u]); if(mx[u]<mx[rt]) rt=u; } void solve(int x,int S) { vis[x]=1; f[0][1]=1; for(int i=h[x];~i;i=e[i].nxt) { if(!vis[e[i].v]) { int hed=0,tail=0,q[maxn]; q[tail++]=e[i].v; fa[e[i].v]=x; dis[e[i].v]=e[i].w; deep[e[i].v]=1; while(hed!=tail) { int now=q[hed++]; int K=deep[now]; if(K>k) break; if(dis[now]>g[K][0]) g[K][0]=dis[now],g[K][1]=0; if(dis[now]==g[K][0]) g[K][1]++; for(int j=h[now];~j;j=e[j].nxt) { int v=e[j].v; if(!vis[e[j].v] && e[j].v!=fa[now]) { fa[v]=now; deep[v]=deep[now]+1; dis[v]=dis[now]+e[j].w; q[tail++]=v; } } } for(int j=1;j<=k;++j) { if(g[j][0]+f[k-j][0]>ans1) ans1=g[j][0]+f[k-j][0],ans2=0; if(g[j][0]+f[k-j][0]==ans1) ans2+=g[j][1]*f[k-j][1]; } for(int j=1;j<=k;++j) { if(g[j][0]>f[j][0]) f[j][0]=g[j][0],f[j][1]=0; if(g[j][0]==f[j][0]) f[j][1]+=g[j][1]; g[j][0]=g[j][1]=0; } } } for(int j=0;j<=k;++j) f[j][0]=f[j][1]=0; for(int j=h[x];~j;j=e[j].nxt) { if(!vis[e[j].v]) { sum=size[e[j].v]; if(size[e[j].v]>size[x]) sum=S-size[x]; rt=0; if(sum>=k) getroot(e[j].v); solve(rt,size[e[j].v]); } } } int main() { scanf("%d%d%d",&n,&m,&k); int x,y,z;k--; for(int i=1;i<=m;++i) { scanf("%d%d%d",&x,&y,&z); v[x].push_back(make_pair(y,z)); v[y].push_back(make_pair(x,z)); } cnt=cnt1=ans1=0; memset(head,-1,sizeof(head)); memset(h,-1,sizeof(h)); memset(vis,0,sizeof(vis)); for(int i=1;i<=n;++i) { sort(v[i].begin(),v[i].end()); for(int j=v[i].size()-1;j>=0;--j) addedge(i,v[i][j].first,v[i][j].second); } dijkstra(); dfs(1); memset(vis,0,sizeof(vis)); rt=0;mx[0]=INF;sum=n; getroot(1); solve(rt,sum); printf("%d %d\n",ans1,ans2); return 0; }