4016: [FJOI2014]最短路径树问题

Time Limit: 5 Sec  Memory Limit: 512 MB
Submit: 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

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;
}

【BZOJ-4016】最短路径树问题 Dijkstra + 点分治的更多相关文章

  1. BZOJ 4016 最短路径树问题 最短路径树构造+点分治

    题目: BZOJ4016最短路径树问题 分析: 大家都说这是一道强行拼出来的题,属于是两种算法的模板题. 我们用dijkstra算法算出1为源点的最短路数组,然后遍历一下建出最短路树. 之后就是裸的点 ...

  2. [BZOJ4016][FJOI2014]最短路径树问题(dijkstra+点分治)

    4016: [FJOI2014]最短路径树问题 Time Limit: 5 Sec  Memory Limit: 512 MBSubmit: 1796  Solved: 625[Submit][Sta ...

  3. luogu 2993 [FJOI2014]最短路径树问题 Dijkstra+点分治

    挺简单的,但是给人一种把两个问题强行弄到一起的感觉. 十分不好写. Code: #include <queue> #include <cstdio> #include < ...

  4. 【BZOJ4016】[FJOI2014]最短路径树问题(点分治,最短路)

    [BZOJ4016][FJOI2014]最短路径树问题(点分治,最短路) 题面 BZOJ 洛谷 题解 首先把最短路径树给构建出来,然后直接点分治就行了. 这个东西似乎也可以长链剖分,然而没有必要. # ...

  5. BZOJ 4016 [FJOI2014]最短路径树问题 (贪心+点分治)

    题目大意:略 传送门 硬是把两个题拼到了一起= = $dijkstra$搜出单源最短路,然后$dfs$建树,如果$dis_{v}=dis_{u}+e.val$,说明这条边在最短路图内,然后像$NOIP ...

  6. HYSBZ - 4016 最短路径树问题 点分治 + 最短路径最小字典序

    题目传送门 题解:首先对于给定的图,需要找到那些从1好点出发然后到x号点的最短路, 如果有多条最短路就要找到字典序最小的路,这样扣完这些边之后就会有一棵树.然后再就是很普通的点分治了. 对于扣边这个问 ...

  7. 【bzoj4016】[FJOI2014]最短路径树问题 堆优化Dijkstra+DFS树+树的点分治

    题目描述 给一个包含n个点,m条边的无向连通图.从顶点1出发,往其余所有点分别走一次并返回. 往某一个点走时,选择总长度最短的路径走.若有多条长度最短的路径,则选择经过的顶点序列字典序最小的那条路径( ...

  8. bzoj 4016 [FJOI2014]最短路径树问题(最短路径树+树分治)

    4016: [FJOI2014]最短路径树问题 Time Limit: 5 Sec  Memory Limit: 512 MBSubmit: 426  Solved: 147[Submit][Stat ...

  9. bzoj 4016: [FJOI2014]最短路径树问题

    bzoj4016 最短路路径问题 Time Limit: 5 Sec Memory Limit: 512 MB Description 给一个包含n个点,m条边的无向连通图.从顶点1出发,往其余所有点 ...

随机推荐

  1. bzoj千题计划211:bzoj1996: [Hnoi2010]chorus 合唱队

    http://www.lydsy.com/JudgeOnline/problem.php?id=1996 f[i][j][0/1] 表示已经排出队形中的[i,j],最后一个插入的人在[i,j]的i或j ...

  2. SQL语句(十二)分组查询

    (十二)分组查询 将数据表中的数据按某种条件分成组,按组显示统计信息 查询各班学生的最大年龄.最小年龄.平均年龄和人数 分组 SELECT <字段名表1> FROM <表名> ...

  3. HDU 4502 吉哥系列故事——临时工计划(一维动态规划)

    题意:吉哥的假期是1到n天,然后有m个工作可以让吉哥选择做,每个工作都有一个开始 t_s  和结束的时间   t_e ,都用天来表示,然后每个工作必须从第一天做到最后一天, 从头到尾做完之后就可以得到 ...

  4. Linux笔记之如何分割文件或管道流:split

    一.简介 在Linux中合并文件可以使用cat命令,后面跟上要合并的文件然后重定向到一个新的文件中即可,甚至可以追加合并.但如果要将一个大文件分割为多个小文件应该如何操作呢? 在Linux的coreu ...

  5. Redis的五大数据类型

    1.String(字符串) String是Redis最基本的类型,一个Key对应一个Value. String类型是二进制安全的,意思是Redis的String可以包含任何数据,比如jpg图片或者序列 ...

  6. android休眠唤醒驱动流程分析【转】

    转自:http://blog.csdn.net/hanmengaidudu/article/details/11777501 标准linux休眠过程: l        power managemen ...

  7. mac安装ocr

    mac安装Tesserocr 安装 Imagemagick 和 Tesseract 库: brew install imagemagick brew install tesseract --all-l ...

  8. Phantomjs 抓取、分析某个页面加载时浏览器发起的所有的子请求

    var page = require('webpage').create(), system = require('system'), address; if (system.args.length ...

  9. 安装Scrapy遇到的坑

    安装过程怕是要吐血,架梯子等等结果被setuptools的版本给坑了. 参考网址: http://blog.csdn.net/YHYR_YCY/article/details/78876148 htt ...

  10. scp拷贝文件报错-bash: scp: command not found

    今天用scp远程传输资料,报错如下: -bash: scp: command not found 在网上搜资料解决办法如下: 安装scp的软件包: # yum install openssh-clie ...