BZOJ 4016: [FJOI2014]最短路径树问题
4016: [FJOI2014]最短路径树问题
Time Limit: 5 Sec Memory Limit: 512 MBSubmit: 845 Solved: 309
[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的路径。
Source
解析:
先用dijkstra跑出最短路网,然后在最短路网上对于每个节点按照孩子编号从小到大跑dfs,就可以得到题意描述中的那棵树。
对于这棵树,定义状态f[i][j]表示从i号点出发经过j个点的最长路径长度,g[i][j]表示最长路径的数量。
转移,分为子数内和跨越子数转移。
优化:在一棵子数内,一条路径只能是向下延伸或跨越2个子树,对于前者dfs解决,对于后者,这显然是一个可以分治的问题,用点分治可以把复杂度降到O(nlogn)即可。
AC代码:
//调了3个小时,不缩行了,直接扔这了 #include<cstdio> #include<cstring> #include<queue> #include<algorithm> #define pir pair<int,int> using namespace std; const int N=1e5+10; vector<pir>G[N]; int n,m,mv,cnt,root,siz,K; int s[N],froot,f[2][N],g[2][N]; bool vis[N]; int to[N<<1],nxt[N<<1],head[N],W[N<<1],totE;; #define add(a,b,c) (to[++totE] = b, nxt[totE] = head[a], W[totE] = c, head[a] = totE) int d[N]; void dijkstra(){ priority_queue<pir,vector<pir>,greater<pir> >q; memset(d,127,sizeof d); q.push(make_pair(d[1]=0,1)); int p,dis,w,v; while(!q.empty()){ pir h=q.top();q.pop(); dis=h.first; p=h.second; if(d[p]^dis) continue; sort(G[p].begin(),G[p].end()); for(int i=0;i<G[p].size();i++){ if(d[v=G[p][i].first]>dis+(w=G[p][i].second)){ q.push(make_pair(d[v]=dis+w,v)); } } } } void Build(int u){ int v; vis[u]=1; for(int i=0;i<G[u].size();i++){ if(!vis[v=G[u][i].first]&&d[v]==d[u]+G[u][i].second){ add(v,u,G[u][i].second); add(u,v,G[u][i].second); Build(v); } } } void getroot(int u,int fa){ s[u]=1; int mx=0,v; for(int it=head[u];it;it=nxt[it]){ if(!vis[v=to[it]]&&(v^fa)){ getroot(v,u); s[u]+=s[v]; if(s[v]>mx) mx=s[v]; } } if(siz-mx>mx) mx=siz-mx; if(froot>mx) root=u,froot=mx; } void dfs(int u,int fa,int dep){ if(dep>K) return ; int v; if(d[u]>g[0][dep]) g[0][dep]=d[u],g[1][dep]=0; if(d[u]>=g[0][dep]) g[1][dep]++; for(int i=head[u];i;i=nxt[i]){ if(!vis[v=to[i]]&&(v^fa)){ d[v]=d[u]+W[i]; dfs(v,u,dep+1); } } } void solve(int u,int S){ vis[u]=1; f[0][0]=0; f[1][0]=1; int v; for(int i=head[u];i;i=nxt[i]){ if(!vis[v=to[i]]){ d[v]=W[i]; dfs(v,u,1); for(int j=1;j<=K;j++){ if(g[0][j]+f[0][K-j]>mv) mv=g[0][j]+f[0][K-j],cnt=0; if(g[0][j]+f[0][K-j]>=mv) cnt+=g[1][j]*f[1][K-j]; } for(int j=1;j<=K;j++){ if(g[0][j]>f[0][j]) f[0][j]=g[0][j],f[1][j]=0; if(g[0][j]>=f[0][j]) f[1][j]+=g[1][j]; g[0][j]=g[1][j]=0; } } } for(int i=0;i<=K;i++) f[0][i]=f[1][i]=0; for(int i=head[u];i;i=nxt[i]){ if(!vis[v=to[i]]){ froot=siz=s[v]; root=0; getroot(v,u); solve(root,s[v]); } } } int main(){ scanf("%d%d%d",&n,&m,&K);--K; for(int a,b,c,i=1;i<=m;i++){ scanf("%d%d%d",&a,&b,&c); G[a].push_back(make_pair(b,c)); G[b].push_back(make_pair(a,c)); } dijkstra(); Build(1); memset(vis,0,sizeof vis); froot=siz=n; getroot(1,root=0); solve(root,n); printf("%d %d\n",mv,cnt); return 0; }