4016: [FJOI2014]最短路径树问题
Time Limit: 5 Sec Memory Limit: 512 MB
Submit: 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
Sample Output
3 4
HINT
对于所有数据n<=30000,m<=60000,2<=K<=n。数据保证最短路径树上至少存在一条长度为K的路径。
Source
[Submit][Status][Discuss]
解析:
先用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;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术